]> git.mxchange.org Git - flightgear.git/commitdiff
Replay upgrade, part I: Add new flight recorder.
authorThorstenB <brehmt@gmail.com>
Sat, 1 Oct 2011 20:41:53 +0000 (22:41 +0200)
committerThorstenB <brehmt@gmail.com>
Sat, 1 Oct 2011 20:41:53 +0000 (22:41 +0200)
projects/VC90/FlightGear/FlightGear.vcproj
src/Aircraft/CMakeLists.txt
src/Aircraft/Makefile.am
src/Aircraft/flightrecorder.cxx [new file with mode: 0644]
src/Aircraft/flightrecorder.hxx [new file with mode: 0644]

index a4c24e46a16bc54562776eaec565d12061479a96..6859879e41d06a3e8b164b3035785c0c5bcbb03c 100644 (file)
                                RelativePath="..\..\..\src\Aircraft\replay.hxx"\r
                                >\r
                        </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\Aircraft\flightrecorder.cxx"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\Aircraft\flightrecorder.hxx"\r
+                               >\r
+                       </File>\r
                </Filter>\r
                <Filter\r
                        Name="Lib_Airports"\r
index 974d66559265d156fd72057616e318ab0c6e0d98..3dcd385c33e67249d2d1c10d51c430789430a0a2 100644 (file)
@@ -3,12 +3,14 @@ include(FlightGearComponent)
 set(SOURCES
        controls.cxx
        replay.cxx
+       flightrecorder.cxx
        )
 
 set(HEADERS
        controls.hxx
        replay.hxx
+       flightrecorder.hxx
        )
 
 
-flightgear_component(Aircraft "${SOURCES}" "${HEADERS}")
\ No newline at end of file
+flightgear_component(Aircraft "${SOURCES}" "${HEADERS}")
index 02b586a9f2f514bc75bec386ea90028a3a41f6cc..c6ab897a2a03cdccb38b9a8f0a54f485aa5dbb24 100644 (file)
@@ -2,6 +2,7 @@ noinst_LIBRARIES = libAircraft.a
 
 libAircraft_a_SOURCES = \
        controls.cxx controls.hxx \
-       replay.cxx replay.hxx
+       replay.cxx replay.hxx \
+       flightrecorder.cxx flightrecorder.hxx
 
 INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
diff --git a/src/Aircraft/flightrecorder.cxx b/src/Aircraft/flightrecorder.cxx
new file mode 100644 (file)
index 0000000..6c3efc0
--- /dev/null
@@ -0,0 +1,544 @@
+// flightrecorder.cxx
+//
+// Written by Thorsten Brehm, started August 2011.
+//
+// Copyright (C) 2011 Thorsten Brehm - brehmt (at) gmail com
+//
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/props/props_io.hxx>
+#include <simgear/misc/ResourceManager.hxx>
+#include <simgear/misc/strutils.hxx>
+#include <simgear/structure/exception.hxx>
+#include <Main/fg_props.hxx>
+#include "flightrecorder.hxx"
+
+using namespace FlightRecorder;
+
+FGFlightRecorder::FGFlightRecorder(const char* pConfigName) :
+    m_RecorderNode(fgGetNode("/sim/flight-recorder", true)),
+    m_TotalRecordSize(0),
+    m_ConfigName(pConfigName)
+{
+}
+
+FGFlightRecorder::~FGFlightRecorder()
+{
+}
+
+void
+FGFlightRecorder::reinit(void)
+{
+    m_ConfigNode = 0;
+
+    m_TotalRecordSize = 0;
+
+    m_CaptureDouble.clear();
+    m_CaptureFloat.clear();
+    m_CaptureInteger.clear();
+    m_CaptureInt16.clear();
+    m_CaptureInt8.clear();
+    m_CaptureBool.clear();
+
+    int Selected = m_RecorderNode->getIntValue(m_ConfigName, 0);
+    SG_LOG(SG_SYSTEMS, SG_INFO, "FlightRecorder: Recorder configuration #" << Selected);
+    if (Selected >= 0)
+        m_ConfigNode = m_RecorderNode->getChild("config", Selected);
+
+    if (!m_ConfigNode.valid())
+        initDefault();
+
+    if (!m_ConfigNode.valid())
+    {
+        SG_LOG(SG_SYSTEMS, SG_ALERT, "FlightRecorder: Configuration is invalid. Flight recorder disabled.");
+    }
+    else
+    {
+        // set name of active flight recorder type 
+        const char* pRecorderName =
+                m_ConfigNode->getStringValue("name",
+                                             "aircraft-specific flight recorder");
+        SG_LOG(SG_SYSTEMS, SG_INFO, "FlightRecorder: Using custom recorder configuration: " << pRecorderName);
+        m_RecorderNode->setStringValue("active-config-name", pRecorderName);
+
+        // get signals
+        initSignalList("double", m_CaptureDouble,  m_ConfigNode );
+        initSignalList("float",  m_CaptureFloat ,  m_ConfigNode );
+        initSignalList("int",    m_CaptureInteger, m_ConfigNode );
+        initSignalList("int16",  m_CaptureInt16  , m_ConfigNode );
+        initSignalList("int8",   m_CaptureInt8   , m_ConfigNode );
+        initSignalList("bool",   m_CaptureBool   , m_ConfigNode );
+    }
+
+    // calculate size of a single record
+    m_TotalRecordSize = sizeof(double)        * 1 /* sim time */        +
+                        sizeof(double)        * m_CaptureDouble.size()  +
+                        sizeof(float)         * m_CaptureFloat.size()   +
+                        sizeof(int)           * m_CaptureInteger.size() +
+                        sizeof(short int)     * m_CaptureInt16.size()   +
+                        sizeof(signed char)   * m_CaptureInt8.size()    +
+                        sizeof(unsigned char) * ((m_CaptureBool.size()+7)/8); // 8 bools per byte
+
+    // expose size of actual flight recorder record
+    m_RecorderNode->setIntValue("record-size", m_TotalRecordSize);
+    SG_LOG(SG_SYSTEMS, SG_INFO, "FlightRecorder: record size is " << m_TotalRecordSize << " bytes");
+}
+
+/** Check if SignalList already contains the given property */
+bool
+FGFlightRecorder::haveProperty(FlightRecorder::TSignalList& SignalList,SGPropertyNode* pProperty)
+{
+    unsigned int Count = SignalList.size();
+    for (unsigned int i=0; i<Count; i++)
+    {
+        if (SignalList[i].Signal.get() == pProperty)
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+/** Check if any signal list already contains the given property */
+bool
+FGFlightRecorder::haveProperty(SGPropertyNode* pProperty)
+{
+    if (haveProperty(m_CaptureDouble,  pProperty))
+        return true;
+    if (haveProperty(m_CaptureFloat,   pProperty))
+        return true;
+    if (haveProperty(m_CaptureInteger, pProperty))
+        return true;
+    if (haveProperty(m_CaptureInt16,   pProperty))
+        return true;
+    if (haveProperty(m_CaptureInt8,    pProperty))
+        return true;
+    if (haveProperty(m_CaptureBool,    pProperty))
+        return true;
+    return false;
+}
+
+/** Read default flight-recorder configuration.
+ * Default should match properties as hard coded for versions up to FG2.4.0. */
+void
+FGFlightRecorder::initDefault(void)
+{
+    // set name of active flight recorder type
+    SG_LOG(SG_SYSTEMS, SG_INFO, "FlightRecorder: No custom configuration. Loading generic default recorder.");
+
+    const char* Path = m_RecorderNode->getStringValue("default-config",NULL);
+    if (!Path)
+    {
+        SG_LOG(SG_SYSTEMS, SG_ALERT, "FlightRecorder: No default flight recorder specified! Check preferences.xml!");
+    }
+    else
+    {
+        SGPath path = globals->resolve_aircraft_path(Path);
+        if (path.isNull())
+        {
+            SG_LOG(SG_SYSTEMS, SG_ALERT, "FlightRecorder: Cannot find file '" << Path << "'.");
+        }
+        else
+        {
+            try
+            {
+                readProperties(path.str(), m_RecorderNode->getChild("config", 0 ,true), 0);
+                m_ConfigNode = m_RecorderNode->getChild("config", 0 ,false);
+            } catch (sg_io_exception &e)
+            {
+                SG_LOG(SG_SYSTEMS, SG_ALERT, "FlightRecorder: Error reading file '" <<
+                        Path << ": " << e.getFormattedMessage());
+            }
+        }
+    }
+}
+
+/** Read signal list below given base node.
+ * Only process properties of given signal type and add all signals to the given list.
+ * This method is called for all supported signal types - properties of each type are
+ * kept in separate lists for efficiency reasons. */
+void
+FGFlightRecorder::initSignalList(const char* pSignalType, TSignalList& SignalList, SGPropertyNode_ptr BaseNode)
+{
+    // clear old signals
+    SignalList.clear();
+
+    processSignalList(pSignalType, SignalList, BaseNode);
+
+    SG_LOG(SG_SYSTEMS, SG_DEBUG, "FlightRecorder: " << SignalList.size() << " signals of type " << pSignalType );
+}
+
+/** Process signal list below given base node.
+ * Only process properties of given signal type and add all signals to the given list.
+ * This method is called for all supported signal types - properties of each type are
+ * kept in separate lists for efficiency reasons. */
+void
+FGFlightRecorder::processSignalList(const char* pSignalType, TSignalList& SignalList, SGPropertyNode_ptr SignalListNode,
+                                    string PropPrefix, int Count)
+{
+    // get the list of signal sources (property paths) for this signal type
+    SGPropertyNode_ptr SignalNode;
+    int Index=0;
+
+    Count = SignalListNode->getIntValue("count",Count);
+    PropPrefix = simgear::strutils::strip(SignalListNode->getStringValue("prefix",PropPrefix.c_str()));
+    if ((!PropPrefix.empty())&&(PropPrefix[PropPrefix.size()-1] != '/'))
+        PropPrefix += "/";
+
+    do
+    {
+        SignalNode = SignalListNode->getChild("signal",Index,false);
+        if (SignalNode.valid()&&
+            (0==strcmp(pSignalType, SignalNode->getStringValue("type","float"))))
+        {
+            string PropertyPath = SignalNode->getStringValue("property","");
+            if (!PropertyPath.empty())
+            {
+                PropertyPath = PropPrefix + PropertyPath;
+                const char* pInterpolation = SignalNode->getStringValue("interpolation","linear");
+
+                // Check if current signal has a "%i" place holder. Otherwise count is 1.
+                string::size_type IndexPos = PropertyPath.find("%i");
+                int SignalCount = Count;
+                if (IndexPos == string::npos)
+                    SignalCount = 1;
+
+                for (int IndexValue=0;IndexValue<SignalCount;IndexValue++)
+                {
+                    string PPath = PropertyPath;
+                    if (IndexPos != string::npos)
+                    {
+                        char strbuf[20];
+                        snprintf(strbuf, 20, "%d", IndexValue);
+                        PPath = PPath.replace(IndexPos,2,strbuf);
+                    }
+                    TCapture Capture;
+                    Capture.Signal = fgGetNode(PPath.c_str(),false);
+                    if (!Capture.Signal.valid())
+                    {
+                        // warn user: we're maybe going to record useless data
+                        // Or maybe the data is only initialized later. Warn anyway, so we can catch useless data. 
+                        SG_LOG(SG_SYSTEMS, SG_INFO, "FlightRecorder: Recording non-existent property '" << PPath << "'.");
+                        Capture.Signal = fgGetNode(PPath.c_str(),true);
+                    }
+
+                    if (0==strcmp(pInterpolation,"discrete"))
+                        Capture.Interpolation = discrete;
+                    else 
+                    if ((0==strcmp(pInterpolation,"angular"))||
+                        (0==strcmp(pInterpolation,"angular-rad")))
+                        Capture.Interpolation = angular_rad;
+                    else
+                    if (0==strcmp(pInterpolation,"angular-deg"))
+                        Capture.Interpolation = angular_deg;
+                    else
+                    if (0==strcmp(pInterpolation,"linear"))
+                        Capture.Interpolation = linear;
+                    else
+                    {
+                        SG_LOG(SG_SYSTEMS, SG_ALERT, "FlightRecorder: Unsupported interpolation type '"
+                                << pInterpolation<< "' of signal '" << PPath << "'");
+                        Capture.Interpolation = linear;
+                    }
+                    if (haveProperty(Capture.Signal))
+                    {
+                        SG_LOG(SG_SYSTEMS, SG_ALERT, "FlightRecorder: Property '"
+                                << PPath << "' specified multiple times. Check flight recorder configuration.");
+                    }
+                    else
+                        SignalList.push_back(Capture);
+                }
+            }
+        }
+        Index++;
+    } while (SignalNode.valid());
+
+    // allow recursive definition of signal lists
+    simgear::PropertyList Nodes = SignalListNode->getChildren("signals");
+    for (unsigned int i=0;i<Nodes.size();i++)
+    {
+        processSignalList(pSignalType, SignalList, Nodes[i], PropPrefix, Count);
+    }
+}
+
+/** Get an empty container for a single capture. */
+FGReplayData*
+FGFlightRecorder::createEmptyRecord(void)
+{
+    if (!m_TotalRecordSize)
+        return NULL;
+    FGReplayData* p = (FGReplayData*) new unsigned char[m_TotalRecordSize];
+    return p;
+}
+
+/** Free given container with capture data. */
+void
+FGFlightRecorder::deleteRecord(FGReplayData* pRecord)
+{
+    delete[] pRecord;
+}
+
+/** Capture data.
+ * When pBuffer==NULL new memory is allocated.
+ * If pBuffer!=NULL memory of given buffer is reused.
+ */
+FGReplayData*
+FGFlightRecorder::capture(double SimTime, FGReplayData* pRecycledBuffer)
+{
+    if (!pRecycledBuffer)
+    {
+        pRecycledBuffer = createEmptyRecord();
+        if (!pRecycledBuffer)
+            return NULL;
+    }
+    unsigned char* pBuffer = (unsigned char*) pRecycledBuffer;
+
+    int Offset = 0;
+    pRecycledBuffer->sim_time = SimTime;
+    Offset += sizeof(double);
+
+    // 64bit aligned data first!
+    {
+        // capture doubles
+        double* pDoubles = (double*) &pBuffer[Offset];
+        unsigned int SignalCount = m_CaptureDouble.size();
+        for (unsigned int i=0; i<SignalCount; i++)
+        {
+            pDoubles[i] = m_CaptureDouble[i].Signal->getDoubleValue();
+        }
+        Offset += SignalCount * sizeof(double);
+    }
+    
+    // 32bit aligned data comes second...
+    {
+        // capture floats
+        float* pFloats = (float*) &pBuffer[Offset];
+        unsigned int SignalCount = m_CaptureFloat.size();
+        for (unsigned int i=0; i<SignalCount; i++)
+        {
+            pFloats[i] = m_CaptureFloat[i].Signal->getFloatValue();
+        }
+        Offset += SignalCount * sizeof(float);
+    }
+    
+    {
+        // capture integers (32bit aligned)
+        int* pInt = (int*) &pBuffer[Offset];
+        unsigned int SignalCount = m_CaptureInteger.size();
+        for (unsigned int i=0; i<SignalCount; i++)
+        {
+            pInt[i] = m_CaptureInteger[i].Signal->getIntValue();
+        }
+        Offset += SignalCount * sizeof(int);
+    }
+    
+    // 16bit aligned data is next...
+    {
+        // capture 16bit short integers
+        short int* pShortInt = (short int*) &pBuffer[Offset];
+        unsigned int SignalCount = m_CaptureInt16.size();
+        for (unsigned int i=0; i<SignalCount; i++)
+        {
+            pShortInt[i] = (short int) m_CaptureInt16[i].Signal->getIntValue();
+        }
+        Offset += SignalCount * sizeof(short int);
+    }
+    
+    // finally: byte aligned data is last...
+    {
+        // capture 8bit chars
+        signed char* pChar = (signed char*) &pBuffer[Offset];
+        unsigned int SignalCount = m_CaptureInt8.size();
+        for (unsigned int i=0; i<SignalCount; i++)
+        {
+            pChar[i] = (signed char) m_CaptureInt8[i].Signal->getIntValue();
+        }
+        Offset += SignalCount * sizeof(signed char);
+    }
+    
+    {
+        // capture 1bit booleans (8bit aligned)
+        unsigned char* pFlags = (unsigned char*) &pBuffer[Offset];
+        unsigned int SignalCount = m_CaptureBool.size();
+        int Size = (SignalCount+7)/8;
+        Offset += Size;
+        memset(pFlags,0,Size);
+        for (unsigned int i=0; i<SignalCount; i++)
+        {
+            if (m_CaptureBool[i].Signal->getBoolValue())
+                pFlags[i>>3] |= 1 << (i&7);
+        }
+    }
+
+    assert(Offset == m_TotalRecordSize);
+
+    return (FGReplayData*) pBuffer;
+}
+
+/** Do interpolation as defined by given interpolation type and weighting ratio. */
+static double
+weighting(TInterpolation interpolation, double ratio, double v1,double v2)
+{
+    switch (interpolation)
+    {
+        case linear:
+            return v1 + ratio*(v2-v1);
+        
+        case angular_deg:
+        {
+            // special handling of angular data
+            double tmp = v2 - v1;
+            if ( tmp > 180 )
+                tmp -= 360;
+            else if ( tmp < -180 )
+                tmp += 360;
+            return v1 + tmp * ratio;
+        }
+
+        case angular_rad:
+        {
+            // special handling of angular data
+            double tmp = v2 - v1;
+            if ( tmp > SGD_PI )
+                tmp -= SGD_2PI;
+            else if ( tmp < -SGD_PI )
+                tmp += SGD_2PI;
+            return v1 + tmp * ratio;
+        }
+
+        case discrete:
+            // fall through
+        default:
+            return v2;
+    }
+}
+
+/** Replay.
+ * Restore all properties with data from given buffer. */
+void
+FGFlightRecorder::replay(double SimTime, const FGReplayData* _pNextBuffer, const FGReplayData* _pLastBuffer)
+{
+    const char* pLastBuffer = (const char*) _pLastBuffer;
+    const char* pBuffer = (const char*) _pNextBuffer;
+    if (!pBuffer)
+        return;
+
+    int Offset = 0;
+    double ratio;
+    if (pLastBuffer)
+    {
+        double NextSimTime = _pNextBuffer->sim_time;
+        double LastSimTime = _pLastBuffer->sim_time;
+        ratio = (SimTime - LastSimTime) / (NextSimTime - LastSimTime);
+    }
+    else
+    {
+        ratio = 1.0;
+    }
+
+    Offset += sizeof(double);
+
+    // 64bit aligned data first!
+    {
+        // restore doubles
+        const double* pDoubles = (const double*) &pBuffer[Offset];
+        const double* pLastDoubles = (const double*) &pLastBuffer[Offset];
+        unsigned int SignalCount = m_CaptureDouble.size();
+        for (unsigned int i=0; i<SignalCount; i++)
+        {
+            double v = pDoubles[i];
+            if (pLastBuffer)
+            {
+                v = weighting(m_CaptureDouble[i].Interpolation, ratio,
+                              pLastDoubles[i], v);
+            }
+            m_CaptureDouble[i].Signal->setDoubleValue(v);
+        }
+        Offset += SignalCount * sizeof(double);
+    }
+
+    // 32bit aligned data comes second...
+    {
+        // restore floats
+        const float* pFloats = (const float*) &pBuffer[Offset];
+        const float* pLastFloats = (const float*) &pLastBuffer[Offset];
+        unsigned int SignalCount = m_CaptureFloat.size();
+        for (unsigned int i=0; i<SignalCount; i++)
+        {
+            float v = pFloats[i];
+            if (pLastBuffer)
+            {
+                v = weighting(m_CaptureFloat[i].Interpolation, ratio,
+                              pLastFloats[i], v);
+            }
+            m_CaptureFloat[i].Signal->setDoubleValue(v);//setFloatValue
+        }
+        Offset += SignalCount * sizeof(float);
+    }
+
+    {
+        // restore integers (32bit aligned)
+        const int* pInt = (const int*) &pBuffer[Offset];
+        unsigned int SignalCount = m_CaptureInteger.size();
+        for (unsigned int i=0; i<SignalCount; i++)
+        {
+            m_CaptureInteger[i].Signal->setIntValue(pInt[i]);
+        }
+        Offset += SignalCount * sizeof(int);
+    }
+
+    // 16bit aligned data is next...
+    {
+        // restore 16bit short integers
+        const short int* pShortInt = (const short int*) &pBuffer[Offset];
+        unsigned int SignalCount = m_CaptureInt16.size();
+        for (unsigned int i=0; i<SignalCount; i++)
+        {
+            m_CaptureInt16[i].Signal->setIntValue(pShortInt[i]);
+        }
+        Offset += SignalCount * sizeof(short int);
+    }
+
+    // finally: byte aligned data is last...
+    {
+        // restore 8bit chars
+        const signed char* pChar = (const signed char*) &pBuffer[Offset];
+        unsigned int SignalCount = m_CaptureInt8.size();
+        for (unsigned int i=0; i<SignalCount; i++)
+        {
+            m_CaptureInt8[i].Signal->setIntValue(pChar[i]);
+        }
+        Offset += SignalCount * sizeof(signed char);
+    }
+
+    {
+        // restore 1bit booleans (8bit aligned)
+        const unsigned char* pFlags = (const unsigned char*) &pBuffer[Offset];
+        unsigned int SignalCount = m_CaptureBool.size();
+        int Size = (SignalCount+7)/8;
+        Offset += Size;
+        for (unsigned int i=0; i<SignalCount; i++)
+        {
+            m_CaptureBool[i].Signal->setBoolValue(0 != (pFlags[i>>3] & (1 << (i&7))));
+        }
+    }
+}
diff --git a/src/Aircraft/flightrecorder.hxx b/src/Aircraft/flightrecorder.hxx
new file mode 100644 (file)
index 0000000..a2045f6
--- /dev/null
@@ -0,0 +1,89 @@
+// flightrecorder.hxx
+//
+// Written by Thorsten Brehm, started August 2011.
+//
+// Copyright (C) 2011 Thorsten Brehm - brehmt (at) gmail com
+//
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef FLIGHTRECORDER_HXX_
+#define FLIGHTRECORDER_HXX_
+
+#include <simgear/props/props.hxx>
+#include "replay.hxx"
+
+namespace FlightRecorder
+{
+
+    typedef enum
+    {
+        discrete    = 0,   // no interpolation
+        linear      = 1,  // linear interpolation
+        angular_rad = 2,  // angular interpolation, value in radians
+        angular_deg = 3   // angular interpolation, value in degrees
+    } TInterpolation;
+
+    typedef struct
+    {
+        SGPropertyNode_ptr  Signal;
+        TInterpolation      Interpolation;
+    } TCapture;
+
+    typedef std::vector<TCapture> TSignalList;
+
+}
+
+class FGFlightRecorder
+{
+public:
+    FGFlightRecorder(const char* pConfigName);
+    virtual ~FGFlightRecorder();
+
+    void            reinit              (void);
+    FGReplayData*   createEmptyRecord   (void);
+    FGReplayData*   capture             (double SimTime, FGReplayData* pRecycledBuffer);
+    void            replay              (double SimTime, const FGReplayData* pNextBuffer,
+                                         const FGReplayData* pLastBuffer = NULL);
+    void            deleteRecord        (FGReplayData* pRecord);
+
+    int             getRecordSize       (void) { return m_TotalRecordSize;}
+
+private:
+    void initDefault(void);
+    void initSignalList(const char* pSignalType, FlightRecorder::TSignalList& SignalList,
+                        SGPropertyNode_ptr BaseNode);
+    void processSignalList(const char* pSignalType, FlightRecorder::TSignalList& SignalList,
+                           SGPropertyNode_ptr SignalListNode,
+                           string PropPrefix="", int Count = 1);
+    bool haveProperty(FlightRecorder::TSignalList& Capture,SGPropertyNode* pProperty);
+    bool haveProperty(SGPropertyNode* pProperty);
+
+    SGPropertyNode_ptr m_RecorderNode;
+    SGPropertyNode_ptr m_ConfigNode;
+
+    FlightRecorder::TSignalList m_CaptureDouble;
+    FlightRecorder::TSignalList m_CaptureFloat;
+    FlightRecorder::TSignalList m_CaptureInteger;
+    FlightRecorder::TSignalList m_CaptureInt16;
+    FlightRecorder::TSignalList m_CaptureInt8;
+    FlightRecorder::TSignalList m_CaptureBool;
+
+    int m_TotalRecordSize;
+    string m_ConfigName;
+};
+
+#endif /* FLIGHTRECORDER_HXX_ */