]> git.mxchange.org Git - flightgear.git/commitdiff
Curt:
authorcurt <curt>
Thu, 17 Jul 2003 18:24:17 +0000 (18:24 +0000)
committercurt <curt>
Thu, 17 Jul 2003 18:24:17 +0000 (18:24 +0000)
I have added a fledgling replay system that records flight data and control
positions during the flight.

I have added an internal command called "replay" which will trigger a replay
of the entire saved flight data set.  This could be bound to a keyboard or
menu command, in fact this entire module is screaming for someone to build
a gui to control playback speed, amount of playback, etc.

This is the initial version so there are kinks that still need to be worked
out, please be patient.

13 files changed:
configure.ac
src/FDM/ExternalNet/ExternalNet.cxx
src/FDM/ExternalPipe/ExternalPipe.cxx
src/Main/Makefile.am
src/Main/fg_commands.cxx
src/Main/fg_init.cxx
src/Main/main.cxx
src/Makefile.am
src/Network/native_ctrls.cxx
src/Network/native_ctrls.hxx
src/Replay/Makefile.am [new file with mode: 0644]
src/Replay/replay.cxx [new file with mode: 0644]
src/Replay/replay.hxx [new file with mode: 0644]

index 1cb94192630bede2a0f347f39d6507338d6ec87e..a98c8fbe893f824fbec0d25ab2f2ad2da38ef194 100644 (file)
@@ -595,6 +595,7 @@ AC_CONFIG_FILES([ \
        src/Network/Makefile \
        src/NetworkOLK/Makefile \
        src/Objects/Makefile \
+       src/Replay/Makefile \
        src/Scenery/Makefile \
        src/Scripting/Makefile \
        src/Sound/Makefile \
index 46129e24e1ce9d1778a8170094841b227be477d8..79c142265f35e60b888819a8efc3b007f5db9b35 100644 (file)
@@ -164,7 +164,7 @@ void FGExternalNet::update( double dt ) {
 
     // Send control positions to remote fdm
     length = sizeof(ctrls);
-    FGProps2NetCtrls( &ctrls );
+    FGProps2NetCtrls( &ctrls, true, true );
     if ( data_client.send( (char *)(& ctrls), length, 0 ) != length ) {
        SG_LOG( SG_IO, SG_DEBUG, "Error writing data." );
     } else {
index c43b99f54a41de5600aa96134a5f8b593fd3af47..312fe1d3de4364b2bd16cef53a66c64724a3beda 100644 (file)
@@ -192,7 +192,7 @@ void FGExternalPipe::update( double dt ) {
 
     // Send control positions to remote fdm
     length = sizeof(ctrls);
-    FGProps2NetCtrls( &ctrls, false );
+    FGProps2NetCtrls( &ctrls, true, false );
     char *ptr = buf;
     *ptr = '2';
     ptr++;
index ccaa40f8697763a91265dca4da9ebaa61a81a1d7..7c737b01851e44e0921f3bf280a0c50da12cdda1 100644 (file)
@@ -85,6 +85,7 @@ fgfs_LDADD = \
        $(MPLAYER_LIBS) \
         $(NETWORK_LIBS) \
        $(top_builddir)/src/Objects/libObjects.a \
+       $(top_builddir)/src/Replay/libReplay.a \
        $(top_builddir)/src/Systems/libSystems.a \
        $(top_builddir)/src/Time/libTime.a \
        $(WEATHER_LIBS) \
index a6d53cd5c3b5cf70362a113b699d410612d447f2..2e20d8830846f3e06a17f79fc552a3dc3d92fa66 100644 (file)
@@ -20,6 +20,7 @@
 #include <GUI/gui.h>
 #include <GUI/new_gui.hxx>
 #include <GUI/dialog.hxx>
+#include <Replay/replay.hxx>
 #include <Scenery/tilemgr.hxx>
 #if defined(HAVE_PLIB_PSL)
 #  include <Scripting/scriptmgr.hxx>
@@ -821,6 +822,29 @@ do_log_level (const SGPropertyNode * arg)
    return true;
 }
 
+/**
+ * Built-in command: replay the FDR buffer
+ */
+static bool
+do_replay (const SGPropertyNode * arg)
+{
+    // freeze the master fdm
+    fgSetBool( "/sim/freeze/master", true );
+    fgSetBool( "/sim/freeze/clock", true );
+    fgSetBool( "/sim/replay/master", true );
+
+    FGReplay *r = (FGReplay *)(globals->get_subsystem( "replay" ));
+
+    fgSetDouble( "/sim/replay/start-time", r->get_start_time() );
+    fgSetDouble( "/sim/replay/end-time", r->get_end_time() );
+    fgSetDouble( "/sim/replay/time", r->get_start_time() );
+
+    cout << "start = " << r->get_start_time()
+         << "  end = " << r->get_end_time() << endl;
+
+    return true;
+}
+
 
 
 \f
@@ -871,6 +895,7 @@ static struct {
     { "dialog-apply", do_dialog_apply },
     { "presets-commit", do_presets_commit },
     { "log-level", do_log_level },
+    { "replay", do_replay },
     { 0, 0 }                   // zero-terminated
 };
 
index 59b7104285ee31c09a13dfb1004cc3ee2b580ffd..2342ed739699238628fd0d15d7358bb5f051692d 100644 (file)
 #include <Navaids/ilslist.hxx>
 #include <Navaids/mkrbeacons.hxx>
 #include <Navaids/navlist.hxx>
+#include <Replay/replay.hxx>
 #include <Scenery/scenery.hxx>
 #include <Scenery/tilemgr.hxx>
 #if defined(HAVE_PLIB_PSL)
@@ -1634,6 +1635,11 @@ bool fgInitSubsystems() {
     globals->add_subsystem("input", new FGInput);
 
 
+    ////////////////////////////////////////////////////////////////////
+    // Initialize the replay subsystem
+    ////////////////////////////////////////////////////////////////////
+    globals->add_subsystem("replay", new FGReplay);
+
     ////////////////////////////////////////////////////////////////////
     // Bind and initialize subsystems.
     ////////////////////////////////////////////////////////////////////
index 7299f0eede5ec40cb9ae4f9b3a2726579bf5d6ec..3b718ab974e546d60652c20fe8a24e42a235599c 100644 (file)
@@ -120,6 +120,7 @@ SG_USING_STD(endl);
 #include <MultiPlayer/multiplayrxmgr.hxx>
 #endif
 
+#include <Replay/replay.hxx>
 #include <Scenery/scenery.hxx>
 #include <Scenery/tilemgr.hxx>
 #ifdef ENABLE_AUDIO_SUPPORT
@@ -165,7 +166,8 @@ sgVec3 rway_ols;
 float scene_nearplane = 0.5f;
 float scene_farplane = 120000.0f;
 
-static double delta_time_sec = 0;
+static double delta_time_sec = 0.0;
+static double replay_dt_sec = 0.0;
 
 
 #ifdef FG_WEATHERCM
@@ -932,6 +934,13 @@ void fgRenderFrame() {
 void fgUpdateTimeDepCalcs() {
     static bool inited = false;
 
+    static const SGPropertyNode *replay
+        = fgGetNode( "/sim/replay/master", true );
+    static SGPropertyNode *replay_time
+        = fgGetNode( "/sim/replay/time", true );
+    static const SGPropertyNode *replay_end_time
+        = fgGetNode( "/sim/replay/end-time", true );
+
     //SG_LOG(SG_FLIGHT,SG_INFO, "Updating time dep calcs()");
 
     fgLIGHT *l = &cur_light_params;
@@ -964,8 +973,15 @@ void fgUpdateTimeDepCalcs() {
             inited = true;
         }
 
-        globals->get_autopilot()->update(delta_time_sec);
-        cur_fdm_state->update(delta_time_sec);
+        if ( ! replay->getBoolValue() ) {
+            globals->get_autopilot()->update(delta_time_sec);
+            cur_fdm_state->update(delta_time_sec);
+        } else {
+            FGReplay *r = (FGReplay *)(globals->get_subsystem( "replay" ));
+            r->replay( replay_time->getDoubleValue() );
+            replay_time->setDoubleValue( replay_time->getDoubleValue()
+                                         + replay_dt_sec );
+        }
     }
 
     globals->get_model_mgr()->update(delta_time_sec);
@@ -1010,6 +1026,8 @@ static void fgMainLoop( void ) {
         = fgGetNode("/sim/freeze/clock", true);
     static const SGPropertyNode *cur_time_override
         = fgGetNode("/sim/time/cur-time-override", true);
+    static const SGPropertyNode *replay
+        = fgGetNode("/sim/replay/master", true);
 
     // Update the elapsed time.
     static bool first_time = true;
@@ -1032,9 +1050,12 @@ static void fgMainLoop( void ) {
     }
 
     delta_time_sec = double(current_time_stamp - last_time_stamp) / 1000000.0;
+    if ( replay->getBoolValue() ) {
+        replay_dt_sec = delta_time_sec;
+    }
     if ( clock_freeze->getBoolValue() ) {
         delta_time_sec = 0;
-    }
+    } 
     last_time_stamp = current_time_stamp;
     globals->inc_sim_time_sec( delta_time_sec );
     SGAnimation::set_sim_time_sec( globals->get_sim_time_sec() );
index ce31e6ebb9789f6ef74c93671d887becf4e05a27..d88af9347d08c9fa2a49669cbebeb127558d7342 100644 (file)
@@ -40,6 +40,7 @@ SUBDIRS = \
        $(MPLAYER_DIRS) \
         $(NETWORK_DIRS) \
         Objects \
+       Replay \
         Scenery \
         $(SCRIPTING_DIRS) \
         Sound \
index 8049297a5acc333323b66f0e1179e8ac022d0c8e..f03bc4446d4745c7fb9813b8857d8f8811069593 100644 (file)
@@ -98,7 +98,9 @@ static void htond (double &x)
 
 
 // Populate the FGNetCtrls structure from the property tree.
-void FGProps2NetCtrls( FGNetCtrls *net, bool net_byte_order ) {
+void FGProps2NetCtrls( FGNetCtrls *net, bool honor_freezes,
+                       bool net_byte_order )
+{
     int i;
     SGPropertyNode * node;
     SGPropertyNode * tempnode;
@@ -192,14 +194,16 @@ void FGProps2NetCtrls( FGNetCtrls *net, bool net_byte_order ) {
     net->magvar = fgGetDouble("/environment/magnetic-variation-deg");
     net->speedup = fgGetInt("/sim/speed-up");
     net->freeze = 0;
-    if ( fgGetBool("/sim/freeze/master") ) {
-        net->freeze |= 0x01;
-    }
-    if ( fgGetBool("/sim/freeze/position") ) {
-        net->freeze |= 0x02;
-    }
-    if ( fgGetBool("/sim/freeze/fuel") ) {
-        net->freeze |= 0x04;
+    if ( honor_freezes ) {
+        if ( fgGetBool("/sim/freeze/master") ) {
+            net->freeze |= 0x01;
+        }
+        if ( fgGetBool("/sim/freeze/position") ) {
+            net->freeze |= 0x02;
+        }
+        if ( fgGetBool("/sim/freeze/fuel") ) {
+            net->freeze |= 0x04;
+        }
     }
 
     if ( net_byte_order ) {
@@ -244,7 +248,9 @@ void FGProps2NetCtrls( FGNetCtrls *net, bool net_byte_order ) {
 
 
 // Update the property tree from the FGNetCtrls structure.
-void FGNetCtrls2Props( FGNetCtrls *net, bool net_byte_order ) {
+void FGNetCtrls2Props( FGNetCtrls *net, bool honor_freezes,
+                       bool net_byte_order )
+{
     int i;
 
     SGPropertyNode * node;
@@ -346,10 +352,12 @@ void FGNetCtrls2Props( FGNetCtrls *net, bool net_byte_order ) {
 
     fgSetInt( "/sim/speed-up", net->speedup );
 
-    node = fgGetNode( "/sim/freeze", true );
-    node->setBoolValue( "master", net->freeze & 0x01 );
-    node->setBoolValue( "position", net->freeze & 0x02 );
-    node->setBoolValue( "fuel", net->freeze & 0x04 );
+    if ( honor_freezes ) {
+        node = fgGetNode( "/sim/freeze", true );
+        node->setBoolValue( "master", net->freeze & 0x01 );
+        node->setBoolValue( "position", net->freeze & 0x02 );
+        node->setBoolValue( "fuel", net->freeze & 0x04 );
+    }
 }
 
 
@@ -361,7 +369,7 @@ bool FGNativeCtrls::process() {
     if ( get_direction() == SG_IO_OUT ) {
        // cout << "size of cur_fdm_state = " << length << endl;
 
-       FGProps2NetCtrls( &net_ctrls );
+       FGProps2NetCtrls( &net_ctrls, true, true );
 
        if ( ! io->write( (char *)(& net_ctrls), length ) ) {
            SG_LOG( SG_IO, SG_ALERT, "Error writing data." );
@@ -371,12 +379,12 @@ bool FGNativeCtrls::process() {
        if ( io->get_type() == sgFileType ) {
            if ( io->read( (char *)(& net_ctrls), length ) == length ) {
                SG_LOG( SG_IO, SG_DEBUG, "Success reading data." );
-               FGNetCtrls2Props( &net_ctrls );
+               FGNetCtrls2Props( &net_ctrls, true, true );
            }
        } else {
            while ( io->read( (char *)(& net_ctrls), length ) == length ) {
                SG_LOG( SG_IO, SG_DEBUG, "Success reading data." );
-               FGNetCtrls2Props( &net_ctrls );
+               FGNetCtrls2Props( &net_ctrls, true, true );
            }
        }
     }
index d64d072007225ae13babc6ce43dbdcd04fe0c6df..82a995aa45180040cf81ae8e46a5ccfd5a8b0659 100644 (file)
@@ -63,10 +63,12 @@ public:
 // Helper functions which may be useful outside this class
 
 // Populate the FGNetCtrls structure from the property tree.
-void FGProps2NetCtrls( FGNetCtrls *net, bool net_byte_order = true );
+void FGProps2NetCtrls( FGNetCtrls *net, bool honor_freezes,
+                       bool net_byte_order );
 
 // Update the property tree from the FGNetCtrls structure.
-void FGNetCtrls2Props( FGNetCtrls *net, bool net_byte_order = true );
+void FGNetCtrls2Props( FGNetCtrls *net, bool honor_freezes,
+                       bool net_byte_order );
 
 
 #endif // _FG_NATIVE_CTRLS_HXX
diff --git a/src/Replay/Makefile.am b/src/Replay/Makefile.am
new file mode 100644 (file)
index 0000000..ed79d1e
--- /dev/null
@@ -0,0 +1,5 @@
+noinst_LIBRARIES = libReplay.a
+
+libReplay_a_SOURCES = replay.hxx replay.cxx
+
+INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
diff --git a/src/Replay/replay.cxx b/src/Replay/replay.cxx
new file mode 100644 (file)
index 0000000..8bc2a08
--- /dev/null
@@ -0,0 +1,339 @@
+// replay.cxx - a system to record and replay FlightGear flights
+//
+// Written by Curtis Olson, started Juley 2003.
+//
+// Copyright (C) 2003  Curtis L. Olson  - curt@flightgear.org
+//
+// 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.
+//
+// $Id$
+
+
+#include <Network/native_ctrls.hxx>
+#include <Network/native_fdm.hxx>
+#include <Network/net_ctrls.hxx>
+#include <Network/net_fdm.hxx>
+
+#include "replay.hxx"
+
+
+/**
+ * Constructor
+ */
+
+FGReplay::FGReplay() {
+}
+
+
+/**
+ * Destructor
+ */
+
+FGReplay::~FGReplay() {
+    // no dynamically allocated memory to free
+}
+
+
+/** 
+ * Initialize the data structures
+ */
+
+void FGReplay::init() {
+    sim_time = 0.0;
+    last_mt_time = 0.0;
+    last_lt_time = 0.0;
+
+    // Make sure all queues are flushed
+    while ( !short_term.empty() ) {
+        short_term.pop_front();
+    }
+    while ( !medium_term.empty() ) {
+        medium_term.pop_front();
+    }
+    while ( !medium_term.empty() ) {
+        medium_term.pop_front();
+    }
+}
+
+
+/** 
+ * Bind to the property tree
+ */
+
+void FGReplay::bind() {
+    // nothing to bind
+}
+
+
+/** 
+ *  Unbind from the property tree
+ */
+
+void FGReplay::unbind() {
+    // nothing to unbind
+}
+
+
+/** 
+ *  Update the saved data
+ */
+
+void FGReplay::update( double dt ) {
+
+    if ( dt <= 0 ) {
+        // don't save data if nothing is going on ...
+
+        return;
+    }
+
+    sim_time += dt;
+
+    // build the replay record
+    FGNetFDM f;
+    FGProps2NetFDM( &f, false );
+
+    FGNetCtrls c;
+    FGProps2NetCtrls( &c, false, false );
+
+    FGReplayData r;
+    r.sim_time = sim_time;
+    r.ctrls = c;
+    r.fdm = f;
+
+    // update the short term list
+    short_term.push_back( r );
+
+    FGReplayData st_front = short_term.front();
+    if ( sim_time - st_front.sim_time > st_list_time ) {
+        while ( sim_time - st_front.sim_time > st_list_time ) {
+            st_front = short_term.front();
+            short_term.pop_front();
+        }
+
+        // update the medium term list
+        if ( sim_time - last_mt_time > 1.0 ) {
+            last_mt_time = sim_time;
+            medium_term.push_back( st_front );
+
+            FGReplayData mt_front = medium_term.front();
+            if ( sim_time - mt_front.sim_time > mt_list_time ) {
+                while ( sim_time - mt_front.sim_time > mt_list_time ) {
+                    mt_front = medium_term.front();
+                    medium_term.pop_front();
+                }
+
+                // update the long term list
+                if ( sim_time - last_lt_time > 10.0 ) {
+                    last_lt_time = sim_time;
+                    long_term.push_back( mt_front );
+
+                    FGReplayData lt_front = long_term.front();
+                    if ( sim_time - lt_front.sim_time > lt_list_time ) {
+                        while ( sim_time - lt_front.sim_time > lt_list_time ) {
+                            lt_front = long_term.front();
+                            long_term.pop_front();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+#if 0
+    cout << "short term size = " << short_term.size()
+         << "  time = " << sim_time - short_term.front().sim_time
+         << endl;
+    cout << "medium term size = " << medium_term.size()
+         << "  time = " << sim_time - medium_term.front().sim_time
+         << endl;
+    cout << "long term size = " << long_term.size()
+         << "  time = " << sim_time - long_term.front().sim_time
+         << endl;
+#endif
+}
+
+
+static double weight( double data1, double data2, double ratio ) {
+    return data1 + ( data2 - data1 ) * ratio;
+}
+
+/** 
+ * given two FGReplayData elements and a time, interpolate between them
+ */
+static void update_fdm( FGReplayData frame ) {
+    FGNetFDM2Props( &frame.fdm, false );
+    FGNetCtrls2Props( &frame.ctrls, false, false );
+}
+
+/** 
+ * given two FGReplayData elements and a time, interpolate between them
+ */
+static FGReplayData interpolate( double time, FGReplayData f1, FGReplayData f2 )
+{
+    FGReplayData result = f1;
+
+    FGNetFDM fdm1 = f1.fdm;
+    FGNetFDM fdm2 = f2.fdm;
+
+    // do some work
+    double ratio = (time - f1.sim_time) / (f2.sim_time - f1.sim_time);
+
+    cout << fdm1.longitude << " " << fdm2.longitude << endl;
+    result.fdm.longitude = weight( fdm1.longitude, fdm2.longitude, ratio );
+    result.fdm.latitude = weight( fdm1.latitude, fdm2.latitude, ratio );
+    result.fdm.altitude = weight( fdm1.altitude, fdm2.altitude, ratio );
+    result.fdm.agl = weight( fdm1.agl, fdm2.agl, ratio );
+    result.fdm.phi = weight( fdm1.phi, fdm2.phi, ratio );
+    result.fdm.theta = weight( fdm1.theta, fdm2.theta, ratio );
+    result.fdm.psi = weight( fdm1.psi, fdm2.psi, ratio );
+
+    return result;
+}
+
+/** 
+ * interpolate a specific time from a specific list
+ */
+static void interpolate( double time, replay_list_type list ) {
+    // sanity checking
+    if ( list.size() == 0 ) {
+        // handle empty list
+        return;
+    } else if ( list.size() == 1 ) {
+        // handle list size == 1
+        update_fdm( list[0] );
+        return;
+    }
+
+    unsigned int last = list.size() - 1;
+    unsigned int first = 0;
+    unsigned int mid = ( last + first ) / 2;
+
+
+    bool done = false;
+    while ( !done ) {
+        // cout << "  " << first << " <=> " << last << endl;
+        if ( last == first ) {
+            done = true;
+        } else if ( list[mid].sim_time < time && list[mid+1].sim_time < time ) {
+            // too low
+            first = mid;
+            mid = ( last + first ) / 2;
+        } else if ( list[mid].sim_time > time && list[mid+1].sim_time > time ) {
+            // too high
+            last = mid;
+            mid = ( last + first ) / 2;
+        } else {
+            done = true;
+        }
+    }
+
+    FGReplayData result = interpolate( time, list[mid], list[mid+1] );
+
+    update_fdm( result );
+}
+
+
+/** 
+ *  Replay a saved frame based on time, interpolate from the two
+ *  nearest saved frames.
+ */
+
+void FGReplay::replay( double time ) {
+    cout << "replay: " << time << " ";
+    // find the two frames to interpolate between
+    double t1, t2;
+
+    if ( short_term.size() > 0 ) {
+        t1 = short_term.back().sim_time;
+        t2 = short_term.front().sim_time;
+        if ( time > t1 ) {
+            // replay the most recent frame
+            update_fdm( short_term.back() );
+            cout << "first frame" << endl;
+        } else if ( time <= t1 && time >= t2 ) {
+            interpolate( time, short_term );
+            cout << "from short term" << endl;
+        } else if ( medium_term.size() > 0 ) {
+            t1 = short_term.front().sim_time;
+            t2 = medium_term.back().sim_time;
+            if ( time <= t1 && time >= t2 ) {
+                FGReplayData result = interpolate( time,
+                                                   medium_term.back(),
+                                                   short_term.front() );
+                update_fdm( result );
+                cout << "from short/medium term" << endl;
+            } else {
+                t1 = medium_term.back().sim_time;
+                t2 = medium_term.front().sim_time;
+                if ( time <= t1 && time >= t2 ) {
+                    interpolate( time, medium_term );
+                    cout << "from medium term" << endl;
+                } else if ( long_term.size() > 0 ) {
+                    t1 = medium_term.front().sim_time;
+                    t2 = long_term.back().sim_time;
+                    if ( time <= t1 && time >= t2 ) {
+                        FGReplayData result = interpolate( time,
+                                                           long_term.back(),
+                                                           medium_term.front());
+                        update_fdm( result );
+                       cout << "from medium/long term" << endl;
+                    } else {
+                        t1 = long_term.back().sim_time;
+                        t2 = long_term.front().sim_time;
+                        if ( time <= t1 && time >= t2 ) {
+                            interpolate( time, long_term );
+                            cout << "from long term" << endl;
+                        } else {
+                            // replay the oldest long term frame
+                            update_fdm( long_term.front() );
+                            cout << "oldest long term frame" << endl;
+                        }
+                    }
+                } else {
+                    // replay the oldest medium term frame
+                    update_fdm( medium_term.front() );
+                    cout << "oldest medium term frame" << endl;
+                }
+            }
+        } else {
+            // replay the oldest short term frame
+            update_fdm( short_term.front() );
+            cout << "oldest short term frame" << endl;
+        }
+    } else {
+        // nothing to replay
+    }
+}
+
+
+double FGReplay::get_start_time() {
+    if ( long_term.size() > 0 ) {
+        return long_term.front().sim_time;
+    } else if ( medium_term.size() > 0 ) {
+        return medium_term.front().sim_time;
+    } else if ( short_term.size() ) {
+        return short_term.front().sim_time;
+    } else {
+        return 0.0;
+    }
+}
+
+double FGReplay::get_end_time() {
+    if ( short_term.size() ) {
+        return short_term.back().sim_time;
+    } else {
+        return 0.0;
+    } 
+}
diff --git a/src/Replay/replay.hxx b/src/Replay/replay.hxx
new file mode 100644 (file)
index 0000000..17d68fb
--- /dev/null
@@ -0,0 +1,96 @@
+// replay.hxx - a system to record and replay FlightGear flights
+//
+// Written by Curtis Olson, started Juley 2003.
+//
+// Copyright (C) 2003  Curtis L. Olson  - curt@flightgear.org
+//
+// 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.
+//
+// $Id$
+
+
+#ifndef _FG_REPLAY_HXX
+#define _FG_REPLAY_HXX 1
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <simgear/compiler.h>
+
+#include <deque>
+
+#include <simgear/math/sg_types.hxx>
+#include <simgear/props/props.hxx>
+
+#include <Network/net_ctrls.hxx>
+#include <Network/net_fdm.hxx>
+#include <Main/fgfs.hxx>
+
+SG_USING_STD(deque);
+
+
+class FGReplayData {
+
+public:
+
+    double sim_time;
+    FGNetFDM fdm;
+    FGNetCtrls ctrls;
+};
+
+typedef deque < FGReplayData > replay_list_type;
+
+
+
+/**
+ * A recording/replay module for FlightGear flights
+ * 
+ */
+
+class FGReplay : public FGSubsystem
+{
+
+public:
+
+    FGReplay ();
+    virtual ~FGReplay();
+
+    virtual void init();
+    virtual void bind();
+    virtual void unbind();
+    virtual void update( double dt );
+
+    void replay( double time );
+    double get_start_time();
+    double get_end_time();
+    
+private:
+
+    static const double st_list_time = 10.0;   // 60 secs of high res data
+    static const double mt_list_time = 30.0;  // 10 mins of 1 fps data
+    static const double lt_list_time = 60.0; // 1 hr of 10 spf data
+
+    double sim_time;
+    double last_mt_time;
+    double last_lt_time;
+
+    replay_list_type short_term;
+    replay_list_type medium_term;
+    replay_list_type long_term;
+};
+
+
+#endif // _FG_REPLAY_HXX