diff --git a/docs-mini/README.logging b/docs-mini/README.logging new file mode 100644 index 000000000..f3be54d0c --- /dev/null +++ b/docs-mini/README.logging @@ -0,0 +1,75 @@ +Logging in FlightGear +--------------------- + +[Note: JSBSim also has its own independent logging facilities, which +are not discussed here.] + +FlightGear can log any property values at any interval to one or more +CSV files (which can be read and graphed using spreadsheets like +Gnumeric or Excel). Logging is defined in the '/logging' subbranch of +the main property tree; under '/logging', each '/log' subbranch +defines a separate log with its own output file and interval. Here is +a simple example that logs the rudder and aileron settings every +second (1000ms) to the file steering.csv: + + + + steering.csv + 1000 + + Rudder + /controls/rudder + + + Ailerons + /controls/aileron + + + + +Each 'log' subbranch contains an optional 'filename' property +(defaults to "fg_log.csv"), an optional 'interval-ms' property +(defaults to 0, which logs every frame), and a series of 'entry' +subbranches. + +Each 'entry' subbranch contains a 'property' property specifying the +name of the property to be logged, and an optional 'title' property +specifying the title to use in the CSV file (defaults to the full path +of the property). The elapsed time in milliseconds since the start of +the simulation is always included as the first entry with the title +"Time", so there is no need to include it explicitly. + +Here's a sample of the logging output for the above log: + + Time,Rudder,Ailerons + 6522,0.000000,0.000000 + 7668,-0.000000,0.000000 + 8702,-0.000000,0.000000 + 9705,-0.000000,0.000000 + 10784,-0.000000,0.000000 + 11792,-0.000000,0.000000 + 12808,-0.000000,-0.210000 + 13826,-0.000000,-0.344000 + 14881,-0.000000,-0.066000 + 15901,-0.000000,-0.806000 + 16943,-0.000000,-0.936000 + 17965,-0.000000,-0.534000 + 19013,-0.000000,-0.294000 + 20044,-0.000000,0.270000 + 21090,-0.000000,-1.000000 + 22097,-0.000000,-0.168000 + +Note that the requested interval is only a minimum; most of the time, +the actual interval is slightly longer than the requested one. + +The easiest way for an end-user to define logs is to put the log in a +separate XML file (usually under the user's home directory), then +refer to it using the --config option, like this: + + fgfs --config=log-config.xml + +The output log files are always relative to the current directory. + +-- + +David Megginson, 2002-03-12 diff --git a/src/Main/Makefile.am b/src/Main/Makefile.am index ad7df67ed..6324e2245 100644 --- a/src/Main/Makefile.am +++ b/src/Main/Makefile.am @@ -47,6 +47,7 @@ fgfs_SOURCES = \ fg_props.cxx fg_props.hxx \ fgfs.cxx fgfs.hxx \ globals.cxx globals.hxx \ + logger.cxx logger.hxx \ model.cxx model.hxx \ options.cxx options.hxx \ splash.cxx splash.hxx \ diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index a78638f0b..375548096 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -116,6 +116,7 @@ #include "fg_props.hxx" #include "options.hxx" #include "globals.hxx" +#include "logger.hxx" #include "viewmgr.hxx" #if defined(FX) && defined(XMESA) @@ -779,6 +780,14 @@ bool fgInitSubsystems( void ) { fgEVENT::FG_EVENT_READY, 30000 ); + //////////////////////////////////////////////////////////////////// + // Initialize the logger. + //////////////////////////////////////////////////////////////////// + + globals->set_logger(new FGLogger); + globals->get_logger()->init(); + globals->get_logger()->bind(); + //////////////////////////////////////////////////////////////////// // Initialize the local time subsystem. //////////////////////////////////////////////////////////////////// diff --git a/src/Main/logger.cxx b/src/Main/logger.cxx new file mode 100644 index 000000000..9dfe7399c --- /dev/null +++ b/src/Main/logger.cxx @@ -0,0 +1,110 @@ +// logger.cxx - log properties. +// Written by David Megginson, started 2002. +// +// This file is in the Public Domain, and comes with no warranty. + +#include "logger.hxx" + +#include +SG_USING_STD(ofstream); +SG_USING_STD(endl); + +#include +SG_USING_STD(string); + +#include + +#include "fg_props.hxx" + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGLogger +//////////////////////////////////////////////////////////////////////// + +FGLogger::FGLogger () +{ +} + +FGLogger::~FGLogger () +{ +} + +void +FGLogger::init () +{ + SGPropertyNode * logging = fgGetNode("/logging"); + if (logging == 0) + return; + + vector children = logging->getChildren("log"); + for (int i = 0; i < children.size(); i++) { + _logs.push_back(Log()); + Log &log = _logs[_logs.size()-1]; + SGPropertyNode * child = children[i]; + string filename = child->getStringValue("filename", "fg_log.csv"); + log.interval_ms = child->getLongValue("interval-ms", 0); + log.output = new ofstream(filename.c_str()); + if (!log.output) { + SG_LOG(SG_INPUT, SG_ALERT, "Cannot write log to " << filename); + continue; + } + vector entries = child->getChildren("entry"); + (*log.output) << "Time"; + for (int j = 0; j < entries.size(); j++) { + SGPropertyNode * entry = entries[j]; + SGPropertyNode * node = + fgGetNode(entry->getStringValue("property"), true); + log.nodes.push_back(node); + (*log.output) << ',' + << entry->getStringValue("title", node->getPath()); + } + (*log.output) << endl; + } +} + +void +FGLogger::bind () +{ +} + +void +FGLogger::unbind () +{ +} + +void +FGLogger::update (int dt) +{ + long elapsed_ms = globals->get_elapsed_time_ms(); + for (int i = 0; i < _logs.size(); i++) { + if ((elapsed_ms - _logs[i].last_time_ms) >= _logs[i].interval_ms) { + _logs[i].last_time_ms = elapsed_ms; + (*_logs[i].output) << globals->get_elapsed_time_ms(); + for (int j = 0; j < _logs[i].nodes.size(); j++) { + (*_logs[i].output) << ',' << _logs[i].nodes[j]->getStringValue(); + } + (*_logs[i].output) << endl; + } + } +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGLogger::Log +//////////////////////////////////////////////////////////////////////// + +FGLogger::Log::Log () + : output(0), + interval_ms(0), + last_time_ms(-99999999999999L) +{ +} + +FGLogger::Log::~Log () +{ + delete output; +} + +// end of logger.cxx diff --git a/src/Main/logger.hxx b/src/Main/logger.hxx new file mode 100644 index 000000000..ef11ef7da --- /dev/null +++ b/src/Main/logger.hxx @@ -0,0 +1,65 @@ +// logger.hxx - log properties. +// Written by David Megginson, started 2002. +// +// This file is in the Public Domain, and comes with no warranty. + +#ifndef __LOGGER_HXX +#define __LOGGER_HXX 1 + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include +SG_USING_STD(ostream); + +#include +SG_USING_STD(vector); + +#include "fgfs.hxx" + + +/** + * Log any property values to any number of CSV files. + */ +class FGLogger : public FGSubsystem +{ +public: + + FGLogger (); + virtual ~FGLogger (); + + // Implementation of FGSubsystem + virtual void init (); + virtual void bind (); + virtual void unbind (); + virtual void update (int dt); + +private: + + /** + * A single instance of a log file (the logger can contain many). + */ + struct Log { + Log (); + virtual ~Log (); + vector nodes; + ostream * output; + long interval_ms; + long last_time_ms; + }; + + vector _logs; + +}; + +#endif // __LOGGER_HXX