src/Network/Makefile \
src/NetworkOLK/Makefile \
src/Objects/Makefile \
+ src/Replay/Makefile \
src/Scenery/Makefile \
src/Scripting/Makefile \
src/Sound/Makefile \
// 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 {
// Send control positions to remote fdm
length = sizeof(ctrls);
- FGProps2NetCtrls( &ctrls, false );
+ FGProps2NetCtrls( &ctrls, true, false );
char *ptr = buf;
*ptr = '2';
ptr++;
$(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) \
#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>
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
{ "dialog-apply", do_dialog_apply },
{ "presets-commit", do_presets_commit },
{ "log-level", do_log_level },
+ { "replay", do_replay },
{ 0, 0 } // zero-terminated
};
#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)
globals->add_subsystem("input", new FGInput);
+ ////////////////////////////////////////////////////////////////////
+ // Initialize the replay subsystem
+ ////////////////////////////////////////////////////////////////////
+ globals->add_subsystem("replay", new FGReplay);
+
////////////////////////////////////////////////////////////////////
// Bind and initialize subsystems.
////////////////////////////////////////////////////////////////////
#include <MultiPlayer/multiplayrxmgr.hxx>
#endif
+#include <Replay/replay.hxx>
#include <Scenery/scenery.hxx>
#include <Scenery/tilemgr.hxx>
#ifdef ENABLE_AUDIO_SUPPORT
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
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;
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);
= 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;
}
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() );
$(MPLAYER_DIRS) \
$(NETWORK_DIRS) \
Objects \
+ Replay \
Scenery \
$(SCRIPTING_DIRS) \
Sound \
// 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;
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 ) {
// 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;
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 );
+ }
}
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." );
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 );
}
}
}
// 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
--- /dev/null
+noinst_LIBRARIES = libReplay.a
+
+libReplay_a_SOURCES = replay.hxx replay.cxx
+
+INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
--- /dev/null
+// 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;
+ }
+}
--- /dev/null
+// 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