From da6568ad50774e241d8157006a0b49baee2d8537 Mon Sep 17 00:00:00 2001 From: ehofman Date: Fri, 17 Feb 2006 09:43:33 +0000 Subject: [PATCH] =?UTF-8?q?Mathias=20Fr=F6hlich:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new multiplayer patch with an extension to transmit some properties with the base package. The properties are transmitted in a way that will not immediately brake the packet format if we need new ones. Even if the maxmimum number needs to be limited somehow, that format might work well until we have an improoved packet format which is even more compact and that does not require to retransmit redundant information with each packet. That part is relatively fresh and based on that what Oliver provides on his multiplayer server web page. The properties are transferred to the client and I have modified the seahawks rudder animation property to use a relative property path to verify that it works appart from the fact that you can see it changing in the property browser. The movement is still a bit jerky, but that can be fixed/tuned later without again braking the packet format. --- configure.ac | 12 - src/AIModel/AIBase.cxx | 13 +- src/AIModel/AIBase.hxx | 2 - src/AIModel/AIManager.cxx | 97 ++-- src/AIModel/AIManager.hxx | 7 +- src/AIModel/AIMultiplayer.cxx | 449 ++++++++------- src/AIModel/AIMultiplayer.hxx | 135 ++--- src/Main/Makefile.am | 4 - src/Main/fg_init.cxx | 5 - src/Main/fg_io.cxx | 7 - src/Main/globals.cxx | 2 +- src/Main/globals.hxx | 8 - src/Main/main.cxx | 16 +- src/Main/options.cxx | 5 - src/Model/acmodel.cxx | 1 - src/MultiPlayer/Makefile.am | 2 +- src/MultiPlayer/mpmessages.hxx | 122 ++-- src/MultiPlayer/multiplaymgr.cxx | 938 +++++++++++++------------------ src/MultiPlayer/multiplaymgr.hxx | 77 ++- src/MultiPlayer/tiny_xdr.cxx | 4 +- src/Network/Makefile.am | 4 - src/Network/multiplay.cxx | 327 ++++------- src/Network/multiplay.hxx | 42 +- 23 files changed, 1000 insertions(+), 1279 deletions(-) diff --git a/configure.ac b/configure.ac index 2e599906e..7af936d5e 100644 --- a/configure.ac +++ b/configure.ac @@ -90,18 +90,6 @@ if test "x$with_logging" = "xno" ; then AC_DEFINE([FG_NDEBUG], 1, [Define for no logging output]) fi -# Specify if we want to build with Multiplayer support -# default to with_network=yes -AC_ARG_WITH(multiplayer, [ --with-multiplayer Include multiplayer support [default=yes]]) - -if test "x$with_multiplayer" = "xno"; then - echo "Building without any kind of multiplayer support" -else - echo "Building with multiplayer support" - AC_DEFINE([FG_MPLAYER_AS], 1, [Define to build with multiplayer support]) -fi -AM_CONDITIONAL(ENABLE_MPLAYER_AS, test "x$with_multiplayer" != "xno") - AC_ARG_ENABLE(sp_fdms, [ --enable-sp-fdms Include special purpose Flight Models], [enable_sp_fdms="$enableval"] ) AC_DEFINE([ENABLE_SP_FMDS], test "x$enable_sp_fdms" = "xyes", [Define to include special purpose FDMs]) AM_CONDITIONAL(ENABLE_SP_FDM, test "x$enable_sp_fdms" != "xno") diff --git a/src/AIModel/AIBase.cxx b/src/AIModel/AIBase.cxx index 3750d695f..6e1afe2d9 100644 --- a/src/AIModel/AIBase.cxx +++ b/src/AIModel/AIBase.cxx @@ -64,7 +64,6 @@ FGAIBase::FGAIBase(object_type ot) invisible = true; no_roll = true; life = 900; - index = 0; delete_me = false; } @@ -74,10 +73,13 @@ FGAIBase::~FGAIBase() { globals->get_scenery()->unregister_placement_transform(aip.getTransform()); globals->get_scenery()->get_scene_graph()->removeKid(aip.getSceneGraph()); } - SGPropertyNode *root = globals->get_props()->getNode("ai/models", true); - root->removeChild(getTypeString(), index); + if (props) { + SGPropertyNode* parent = props->getParent(); + if (parent) + parent->removeChild(props->getName(), props->getIndex()); + } delete fp; - fp = NULL; + fp = 0; } @@ -121,7 +123,8 @@ bool FGAIBase::init() { SGPropertyNode *root = globals->get_props()->getNode("ai/models", true); - index = manager->getNum(_otype) - 1; + unsigned index = root->getChildren(getTypeString()).size(); + props = root->getNode(getTypeString(), index, true); if (!model_path.empty()) { diff --git a/src/AIModel/AIBase.hxx b/src/AIModel/AIBase.hxx index f71a83f86..cbd6681f5 100644 --- a/src/AIModel/AIBase.hxx +++ b/src/AIModel/AIBase.hxx @@ -131,8 +131,6 @@ protected: void CalculateMach(); double UpdateRadar(FGAIManager* manager); - int index; - static int _newAIModelID(); private: diff --git a/src/AIModel/AIManager.cxx b/src/AIModel/AIManager.cxx index af7e334ee..5fe3d3dc2 100644 --- a/src/AIModel/AIManager.cxx +++ b/src/AIModel/AIManager.cxx @@ -34,10 +34,8 @@ #include "AIMultiplayer.hxx" FGAIManager::FGAIManager() { - for (int i=0; i < FGAIBase::MAX_OBJECTS; i++) - numObjects[i] = 0; _dt = 0.0; - scenario_filename = ""; + mNumAiModels = 0; } FGAIManager::~FGAIManager() { @@ -88,7 +86,8 @@ void FGAIManager::reinit() { void FGAIManager::bind() { root = globals->get_props()->getNode("ai/models", true); - root->tie("count", SGRawValuePointer(&numObjects[0])); + root->tie("count", SGRawValueMethods(*this, + &FGAIManager::getNumAiObjects)); } @@ -99,42 +98,34 @@ void FGAIManager::unbind() { void FGAIManager::update(double dt) { - // initialize these for finding nearest thermals - range_nearest = 10000.0; - strength = 0.0; - FGTrafficManager *tmgr = (FGTrafficManager*) globals->get_subsystem("Traffic Manager"); + // initialize these for finding nearest thermals + range_nearest = 10000.0; + strength = 0.0; + if (!enabled) + return; - if (!enabled) - return; + FGTrafficManager *tmgr = (FGTrafficManager*) globals->get_subsystem("Traffic Manager"); + _dt = dt; - _dt = dt; - - ai_list_iterator ai_list_itr = ai_list.begin(); - while(ai_list_itr != ai_list.end()) { - if ((*ai_list_itr)->getDie()) { - tmgr->release((*ai_list_itr)->getID()); - --numObjects[(*ai_list_itr)->getType()]; - --numObjects[0]; - (*ai_list_itr)->unbind(); - if ( ai_list_itr == ai_list.begin() ) { - ai_list.erase(ai_list_itr); - ai_list_itr = ai_list.begin(); - continue; - } else { - ai_list.erase(ai_list_itr--); - } - } else { - fetchUserState(); - if ((*ai_list_itr)->isa(FGAIBase::otThermal)) { - FGAIBase *base = *ai_list_itr; - processThermal((FGAIThermal*)base); - } else { - (*ai_list_itr)->update(_dt); - } - } - ++ai_list_itr; - } - wind_from_down_node->setDoubleValue( strength ); // for thermals + ai_list_iterator ai_list_itr = ai_list.begin(); + while(ai_list_itr != ai_list.end()) { + if ((*ai_list_itr)->getDie()) { + tmgr->release((*ai_list_itr)->getID()); + --mNumAiModels; + (*ai_list_itr)->unbind(); + ai_list_itr = ai_list.erase(ai_list_itr); + } else { + fetchUserState(); + if ((*ai_list_itr)->isa(FGAIBase::otThermal)) { + FGAIBase *base = *ai_list_itr; + processThermal((FGAIThermal*)base); + } else { + (*ai_list_itr)->update(_dt); + } + ++ai_list_itr; + } + } + wind_from_down_node->setDoubleValue( strength ); // for thermals } void @@ -142,27 +133,29 @@ FGAIManager::attach(SGSharedPtr model) { model->setManager(this); ai_list.push_back(model); - ++numObjects[0]; - ++numObjects[model->getType()]; + ++mNumAiModels; model->init(); model->bind(); } void FGAIManager::destroyObject( int ID ) { - ai_list_iterator ai_list_itr = ai_list.begin(); - while(ai_list_itr != ai_list.end()) { - if ((*ai_list_itr)->getID() == ID) { - --numObjects[0]; - --numObjects[(*ai_list_itr)->getType()]; - (*ai_list_itr)->unbind(); - ai_list.erase(ai_list_itr); - break; - } - ++ai_list_itr; - } + ai_list_iterator ai_list_itr = ai_list.begin(); + while(ai_list_itr != ai_list.end()) { + if ((*ai_list_itr)->getID() == ID) { + --mNumAiModels; + (*ai_list_itr)->unbind(); + ai_list_itr = ai_list.erase(ai_list_itr); + } else + ++ai_list_itr; + } } +int +FGAIManager::getNumAiObjects(void) const +{ + return mNumAiModels; +} void FGAIManager::fetchUserState( void ) { user_latitude = user_latitude_node->getDoubleValue(); @@ -174,8 +167,6 @@ void FGAIManager::fetchUserState( void ) { user_speed = user_speed_node->getDoubleValue() * 0.592484; wind_from_east = wind_from_east_node->getDoubleValue(); wind_from_north = wind_from_north_node->getDoubleValue(); - - } diff --git a/src/AIModel/AIManager.hxx b/src/AIModel/AIManager.hxx index 0ed6c9fcb..9686ba009 100644 --- a/src/AIModel/AIManager.hxx +++ b/src/AIModel/AIManager.hxx @@ -95,9 +95,7 @@ public: inline double get_wind_from_east() const {return wind_from_east; } inline double get_wind_from_north() const {return wind_from_north; } - inline int getNum( FGAIBase::object_type ot ) const { - return (0 < ot && ot < FGAIBase::MAX_OBJECTS) ? numObjects[ot] : numObjects[0]; - } + int getNumAiObjects(void) const; void processScenario( const string &filename ); @@ -112,7 +110,8 @@ public: private: bool enabled; - int numObjects[FGAIBase::MAX_OBJECTS]; + int mNumAiModels; + SGPropertyNode* root; SGPropertyNode* wind_from_down_node; SGPropertyNode* user_latitude_node; diff --git a/src/AIModel/AIMultiplayer.cxx b/src/AIModel/AIMultiplayer.cxx index 7b6097e1f..023ba185c 100755 --- a/src/AIModel/AIMultiplayer.cxx +++ b/src/AIModel/AIMultiplayer.cxx @@ -3,7 +3,6 @@ // Based on FGAIAircraft // Written by David Culp, started October 2003. // Also by Gregor Richards, started December 2005. -// With additions by Vivian Meazza, January 2006 // // Copyright (C) 2003 David P. Culp - davidculp2@comcast.net // Copyright (C) 2005 Gregor Richards @@ -26,51 +25,18 @@ # include #endif -#include -#include
-#include
-#include
-#include -#include -#include #include -#include -#include -#ifdef _MSC_VER -# include -# define finite _finite -#elif defined(__sun) || defined(sgi) -# include -#endif - -SG_USING_STD(string); #include "AIMultiplayer.hxx" - static string tempReg; +// #define SG_DEBUG SG_ALERT FGAIMultiplayer::FGAIMultiplayer() : FGAIBase(otMultiplayer) { - _time_node = fgGetNode("/sim/time/elapsed-sec", true); + no_roll = false; - //initialise values - speedN = speedE = rateH = rateR = rateP = 0.0; - raw_hdg = hdg; - raw_roll = roll; - raw_pitch = pitch; - raw_speed_east_deg_sec = speedE / ft_per_deg_lon; - raw_speed_north_deg_sec = speedN / ft_per_deg_lat; - raw_lon = damp_lon = pos.lon(); - raw_lat = damp_lat = pos.lat(); - raw_alt = damp_alt = pos.elev() / SG_FEET_TO_METER; - - //Exponentially weighted moving average time constants - speed_north_deg_sec_constant = speed_east_deg_sec_constant = 0.1; - alt_constant = 0.1; - lat_constant = 0.05; - lon_constant = 0.05; - hdg_constant = 0.1; - roll_constant = 0.1; - pitch_constant = 0.1; + mTimeOffsetSet = false; + mAllowExtrapolation = true; + mLagAdjustSystemSpeed = 10; } @@ -83,192 +49,273 @@ bool FGAIMultiplayer::init() { void FGAIMultiplayer::bind() { FGAIBase::bind(); - props->setStringValue("callsign", company.c_str()); - - props->tie("controls/constants/roll", - SGRawValuePointer(&roll_constant)); - props->tie("controls/constants/pitch", - SGRawValuePointer(&pitch_constant)); - props->tie("controls/constants/hdg", - SGRawValuePointer(&hdg_constant)); - props->tie("controls/constants/altitude", - SGRawValuePointer(&alt_constant)); - /*props->tie("controls/constants/speedE", - SGRawValuePointer(&speed_east_deg_sec_constant)); - props->tie("controls/constants/speedN", - SGRawValuePointer(&speed_north_deg_sec_constant));*/ - props->tie("controls/constants/lat", - SGRawValuePointer(&lat_constant)); - props->tie("controls/constants/lon", - SGRawValuePointer(&lon_constant)); - props->tie("surface-positions/rudder-pos-norm", - SGRawValuePointer(&rudder)); - props->tie("surface-positions/elevator-pos-norm", - SGRawValuePointer(&elevator)); - props->tie("velocities/speedE-fps", - SGRawValuePointer(&speedE)); +#define AIMPROProp(type, name) \ +SGRawValueMethods(*this, &FGAIMultiplayer::get##name) - props->setDoubleValue("sim/current-view/view-number", 1); - -} +#define AIMPRWProp(type, name) \ +SGRawValueMethods(*this, \ + &FGAIMultiplayer::get##name, &FGAIMultiplayer::set##name) -void FGAIMultiplayer::setCompany(string comp) { - company = comp; - if (props) - props->setStringValue("callsign", company.c_str()); + props->tie("callsign", AIMPROProp(const char *, CallSign)); + props->tie("controls/allow-extrapolation", + AIMPRWProp(bool, AllowExtrapolation)); + props->tie("controls/lag-adjust-system-speed", + AIMPRWProp(double, LagAdjustSystemSpeed)); + +#undef AIMPROProp +#undef AIMPRWProp } void FGAIMultiplayer::unbind() { FGAIBase::unbind(); - - props->untie("controls/constants/roll"); - props->untie("controls/constants/pitch"); - props->untie("controls/constants/hdg"); - props->untie("controls/constants/altitude"); - /*props->untie("controls/constants/speedE"); - props->untie("controls/constants/speedN");*/ - props->untie("controls/constants/lat"); - props->untie("controls/constants/lon"); - props->untie("surface-positions/rudder-pos-norm"); - props->untie("surface-positions/elevator-pos-norm"); - props->untie("velocities/speedE-fps"); + + props->untie("callsign"); + props->untie("controls/allow-extrapolation"); + props->untie("controls/lag-adjust-system-speed"); } +void FGAIMultiplayer::update(double dt) +{ + if (dt <= 0) + return; -void FGAIMultiplayer::update(double dt) { + FGAIBase::update(dt); - FGAIBase::update(dt); - Run(dt); - Transform(); -} + // Check if we already got data + if (mMotionInfo.empty()) + return; + + // The current simulation time we need to update for, + // note that the simulation time is updated before calling all the + // update methods. Thus it contains the time intervals *end* time + double curtime = globals->get_sim_time_sec(); + + // Get the last available time + MotionInfo::reverse_iterator it = mMotionInfo.rbegin(); + double curentPkgTime = it->second.time; + + // Dynamically optimize the time offset between the feeder and the client + // Well, 'dynamically' means that the dynamic of that update must be very + // slow. You would otherwise notice huge jumps in the multiplayer models. + // The reason is that we want to avoid huge extrapolation times since + // extrapolation is highly error prone. For that we need something + // approaching the average latency of the packets. This first order lag + // component will provide this. We just take the error of the currently + // requested time to the most recent available packet. This is the + // target we want to reach in average. + double lag = it->second.lag; + if (!mTimeOffsetSet) { + mTimeOffsetSet = true; + mTimeOffset = curentPkgTime - curtime - lag; + } else { + double offset = curentPkgTime - curtime - lag; + if (!mAllowExtrapolation && offset + lag < mTimeOffset) { + mTimeOffset = offset; + SG_LOG(SG_GENERAL, SG_DEBUG, "Resetting time offset adjust system to " + "avoid extrapolation: time offset = " << mTimeOffset); + } else { + // the error of the offset, respectively the negative error to avoid + // a minus later ... + double err = offset - mTimeOffset; + // limit errors leading to shorter lag values somehow, that is late + // arriving packets will pessimize the overall lag much more than + // early packets will shorten the overall lag + double sysSpeed; + if (err < 0) { + // Ok, we have some very late packets and nothing newer increase the + // lag by the given speedadjust + sysSpeed = mLagAdjustSystemSpeed*err; + } else { + // We have a too pessimistic display delay shorten that a small bit + sysSpeed = SGMiscd::min(0.1*err*err, 0.5); + } + + // simple euler integration for that first order system including some + // overshooting guard to prevent to aggressive system speeds + // (stiff systems) to explode the systems state + double systemIncrement = dt*sysSpeed; + if (fabs(err) < fabs(systemIncrement)) + systemIncrement = err; + mTimeOffset += systemIncrement; + + SG_LOG(SG_GENERAL, SG_DEBUG, "Offset adjust system: time offset = " + << mTimeOffset << ", expected longitudinal position error due to " + " current adjustment of the offset: " + << fabs(norm(it->second.linearVel)*systemIncrement)); + } + } -void FGAIMultiplayer::Run(double dt) { - - // strangely, this is called with a dt of 0 quite often + // Compute the time in the feeders time scale which fits the current time + // we need to + double tInterp = curtime + mTimeOffset; - SG_LOG( SG_GENERAL, SG_DEBUG, "AIMultiplayer::main loop dt " << dt ) ; + SGVec3d ecPos; + SGQuatf ecOrient; + if (tInterp <= curentPkgTime) { + // Ok, we need a time prevous to the last available packet, + // that is good ... - //if (dt == 0) return; - - //FGAIMultiplayer::dt = dt; + // Find the first packet before the target time + MotionInfo::iterator nextIt = mMotionInfo.upper_bound(tInterp); + if (nextIt == mMotionInfo.begin()) { + SG_LOG(SG_GENERAL, SG_DEBUG, "Taking oldest packet!"); + // We have no packet before the target time, just use the first one + MotionInfo::iterator firstIt = mMotionInfo.begin(); + ecPos = firstIt->second.position; + ecOrient = firstIt->second.orientation; - //double rhr, rha; // "real" heading radius/angle + std::vector::const_iterator firstPropIt; + std::vector::const_iterator firstPropItEnd; + firstPropIt = firstIt->second.properties.begin(); + firstPropItEnd = firstIt->second.properties.end(); + while (firstPropIt != firstPropItEnd) { + float val = firstPropIt->value; + PropertyMap::iterator pIt = mPropertyMap.find(firstPropIt->id); + if (pIt != mPropertyMap.end()) + pIt->second->setFloatValue(val); + ++firstPropIt; + } - // get the current sim elapsed time - double time =_time_node->getDoubleValue(); //secs - - dt = 0; - - //calulate the time difference, dt. Then use this value to extrapolate position and orientation - dt = time - time_stamp; - - SG_LOG(SG_GENERAL, SG_DEBUG, "time: " - << time << " timestamp: " << time_stamp << " dt: " << dt << " freq Hz: " << 1/dt); - - // change heading/roll/pitch - raw_hdg = hdg + rateH * dt; - raw_roll = roll + rateR * dt; - raw_pitch = pitch + rateP * dt; - - //apply lowpass filters - hdg = (raw_hdg * hdg_constant) + (hdg * (1 - hdg_constant)); - roll = (raw_roll * roll_constant) + (roll * (1 - roll_constant)); - pitch = (raw_pitch * pitch_constant) + (pitch * (1 - pitch_constant)); - - /*cout << "raw roll " << raw_roll <<" damp hdg " << roll << endl; - cout << "raw hdg" << raw_hdg <<" damp hdg " << hdg << endl; - cout << "raw pitch " << raw_pitch <<" damp pitch " << pitch << endl;*/ - - // sanitize HRP - while (hdg < 0) hdg += 360; - while (hdg >= 360) hdg -= 360; - while (roll <= -180) roll += 360; - while (roll > 180) roll -= 360; - while (pitch <= -180) pitch += 360; - while (pitch > 180) pitch -= 360; + } else { + // Ok, we have really found something where our target time is in between + // do interpolation here + MotionInfo::iterator prevIt = nextIt; + --prevIt; - // calculate the new accelerations by change in the rate of heading - /*rhr = sqrt(pow(accN,2) + pow(accE,2)); - rha = atan2(accN, accE); - rha += rateH * dt; - accN = sin(rha); - accE = cos(rha);*/ - - // calculate new speed by acceleration - speedN += accN * dt; - speedE += accE * dt; - speedD += accD * dt; + // Interpolation coefficient is between 0 and 1 + double intervalStart = prevIt->second.time; + double intervalEnd = nextIt->second.time; + double intervalLen = intervalEnd - intervalStart; + double tau = (tInterp - intervalStart)/intervalLen; - // convert speed to degrees per second - // 1.686 - speed_north_deg_sec = speedN / ft_per_deg_lat; - speed_east_deg_sec = speedE / ft_per_deg_lon; + SG_LOG(SG_GENERAL, SG_DEBUG, "Multiplayer vehicle interpolation: [" + << intervalStart << ", " << intervalEnd << "], intervalLen = " + << intervalLen << ", interpolation parameter = " << tau); + + // Here we do just linear interpolation on the position + ecPos = ((1-tau)*prevIt->second.position + tau*nextIt->second.position); + ecOrient = interpolate((float)tau, prevIt->second.orientation, + nextIt->second.orientation); + + if (prevIt->second.properties.size() + == nextIt->second.properties.size()) { + std::vector::const_iterator prevPropIt; + std::vector::const_iterator prevPropItEnd; + std::vector::const_iterator nextPropIt; + std::vector::const_iterator nextPropItEnd; + prevPropIt = prevIt->second.properties.begin(); + prevPropItEnd = prevIt->second.properties.end(); + nextPropIt = nextIt->second.properties.begin(); + nextPropItEnd = nextIt->second.properties.end(); + while (prevPropIt != prevPropItEnd) { + float val = (1-tau)*prevPropIt->value + tau*nextPropIt->value; + PropertyMap::iterator pIt = mPropertyMap.find(prevPropIt->id); + if (pIt != mPropertyMap.end()) + pIt->second->setFloatValue(val); + ++prevPropIt; + ++nextPropIt; + } + } + + // Now throw away too old data + if (prevIt != mMotionInfo.begin()) { + --prevIt; + mMotionInfo.erase(mMotionInfo.begin(), prevIt); + } + } + } else { + // Ok, we need to predict the future, so, take the best data we can have + // and do some eom computation to guess that for now. + FGExternalMotionData motionInfo = it->second; + + // The time to predict, limit to 5 seconds + double t = tInterp - motionInfo.time; + t = SGMisc::min(t, 5); + + SG_LOG(SG_GENERAL, SG_DEBUG, "Multiplayer vehicle extrapolation: " + "extrapolation time = " << t); + + // Do a few explicit euler steps with the constant acceleration's + // This must be sufficient ... + ecPos = motionInfo.position; + ecOrient = motionInfo.orientation; + SGVec3f linearVel = motionInfo.linearVel; + SGVec3f angularVel = motionInfo.angularVel; + while (0 < t) { + double h = 1e-1; + if (t < h) + h = t; + + SGVec3d ecVel = toVec3d(ecOrient.backTransform(linearVel)); + ecPos += h*ecVel; + ecOrient += h*ecOrient.derivative(angularVel); + + linearVel += h*(cross(linearVel, angularVel) + motionInfo.linearAccel); + angularVel += h*motionInfo.angularAccel; + + t -= h; + } + + std::vector::const_iterator firstPropIt; + std::vector::const_iterator firstPropItEnd; + firstPropIt = it->second.properties.begin(); + firstPropItEnd = it->second.properties.end(); + while (firstPropIt != firstPropItEnd) { + float val = firstPropIt->value; + PropertyMap::iterator pIt = mPropertyMap.find(firstPropIt->id); + if (pIt != mPropertyMap.end()) + pIt->second->setFloatValue(val); + ++firstPropIt; + } + } - // calculate new position by speed - raw_lat = pos.lat() + speed_north_deg_sec * dt; - raw_lon = pos.lon() + speed_east_deg_sec * dt ; - raw_alt = (pos.elev() / SG_FEET_TO_METER) + (speedD * dt); - - //apply lowpass filters if the difference is small - if ( fabs ( pos.lat() - raw_lat) < 0.001 ) { - SG_LOG(SG_GENERAL, SG_DEBUG,"lat lowpass filter"); - damp_lat = (raw_lat * lat_constant) + (damp_lat * (1 - lat_constant)); - }else { - // skip the filter - SG_LOG(SG_GENERAL, SG_DEBUG,"lat high pass filter"); - damp_lat = raw_lat; - } - - if ( fabs ( pos.lon() - raw_lon) < 0.001 ) { - SG_LOG(SG_GENERAL, SG_DEBUG,"lon lowpass filter"); - damp_lon = (raw_lon * lon_constant) + (damp_lon * (1 - lon_constant)); - }else { - // skip the filter - SG_LOG(SG_GENERAL, SG_DEBUG,"lon high pass filter"); - damp_lon = raw_lon; - } + // extract the position + SGGeod geod = ecPos; + pos.setlat(geod.getLatitudeDeg()); + pos.setlon(geod.getLongitudeDeg()); + pos.setelev(geod.getElevationM()); + + // The quaternion rotating from the earth centered frame to the + // horizontal local frame + SGQuatf qEc2Hl = SGQuatf::fromLonLat((float)geod.getLongitudeRad(), + (float)geod.getLatitudeRad()); + // The orientation wrt the horizontal local frame + SGQuatf hlOr = conj(qEc2Hl)*ecOrient; + float hDeg, pDeg, rDeg; + hlOr.getEulerDeg(hDeg, pDeg, rDeg); + hdg = hDeg; + roll = rDeg; + pitch = pDeg; - if ( fabs ( (pos.elev()/SG_FEET_TO_METER) - raw_alt) < 10 ) { - SG_LOG(SG_GENERAL, SG_DEBUG,"alt lowpass filter"); - damp_alt = (raw_alt * alt_constant) + (damp_alt * (1 - alt_constant)); - }else { - // skip the filter - SG_LOG(SG_GENERAL, SG_DEBUG,"alt high pass filter"); - damp_alt = raw_alt; - } - - // cout << "raw lat" << raw_lat <<" damp lat " << damp_lat << endl; - //cout << "raw lon" << raw_lon <<" damp lon " << damp_lon << endl; - //cout << "raw alt" << raw_alt <<" damp alt " << damp_alt << endl; - - // set new position - pos.setlat( damp_lat ); - pos.setlon( damp_lon ); - pos.setelev( damp_alt * SG_FEET_TO_METER ); + SG_LOG(SG_GENERAL, SG_DEBUG, "Multiplayer position and orientation: " + << geod << ", " << hlOr); - //save the values - time_stamp = time; - - //###########################// - // do calculations for radar // - //###########################// - //double range_ft2 = UpdateRadar(manager); + //###########################// + // do calculations for radar // + //###########################// + UpdateRadar(manager); + + Transform(); } -void FGAIMultiplayer::setTimeStamp() -{ - // this function sets the timestamp as the sim elapsed time - time_stamp = _time_node->getDoubleValue(); //secs - - //calculate the elapsed time since the latst update for display purposes only - double elapsed_time = time_stamp - last_time_stamp; - - SG_LOG( SG_GENERAL, SG_DEBUG, " net input time s" << time_stamp << " freq Hz: " << 1/elapsed_time ) ; - - //save the values - last_time_stamp = time_stamp; +void +FGAIMultiplayer::addMotionInfo(const FGExternalMotionData& motionInfo, + long stamp) +{ + mLastTimestamp = stamp; + // Drop packets arriving out of order + if (!mMotionInfo.empty() && motionInfo.time < mMotionInfo.rbegin()->first) + return; + mMotionInfo[motionInfo.time] = motionInfo; +} + +void +FGAIMultiplayer::setDoubleProperty(const std::string& prop, double val) +{ + SGPropertyNode* pNode = props->getChild(prop.c_str(), true); + pNode->setDoubleValue(val); } diff --git a/src/AIModel/AIMultiplayer.hxx b/src/AIModel/AIMultiplayer.hxx index e931cb60a..fed80cfdb 100755 --- a/src/AIModel/AIMultiplayer.hxx +++ b/src/AIModel/AIMultiplayer.hxx @@ -1,7 +1,6 @@ // FGAIMultiplayer - AIBase derived class creates an AI multiplayer aircraft // // Written by David Culp, started October 2003. -// With additions by Vivian Meazza // // Copyright (C) 2003 David P. Culp - davidculp2@comcast.net // @@ -22,82 +21,72 @@ #ifndef _FG_AIMultiplayer_HXX #define _FG_AIMultiplayer_HXX -#include "AIManager.hxx" +#include +#include + +#include #include "AIBase.hxx" -//#include -//#include - -#include -SG_USING_STD(string); - - class FGAIMultiplayer : public FGAIBase { - - public: - FGAIMultiplayer(); - ~FGAIMultiplayer(); - - bool init(); - virtual void bind(); - virtual void unbind(); - void update(double dt); - - - void setSpeedN(double sn); - void setSpeedE(double se); - void setSpeedD(double sd); - void setAccN(double an); - void setAccE(double ae); - void setAccD(double ad); - void setRateH(double rh); - void setRateR(double rr); - void setRateP(double rp); - void setRudder( double r ) { rudder = r;} - void setElevator( double e ) { elevator = e; } - void setLeftAileron( double la ) { left_aileron = la; } - void setRightAileron( double ra ) { right_aileron = ra; } - void setTimeStamp(); - - inline SGPropertyNode *FGAIMultiplayer::getProps() { return props; } - - void setAcType(string ac) { acType = ac; }; - void setCompany(string comp); - - virtual const char* getTypeString(void) const { return "multiplayer"; } - - double dt; - double speedN, speedE, speedD; - double rateH, rateR, rateP; - double raw_hdg , raw_roll , raw_pitch ; - double raw_speed_north_deg_sec, raw_speed_east_deg_sec; - double raw_lat, damp_lat, lat_constant; - double raw_lon, damp_lon, lon_constant; - double raw_alt, damp_alt, alt_constant; - double hdg_constant, roll_constant, pitch_constant; - double speed_north_deg_sec_constant, speed_east_deg_sec_constant; - double speed_north_deg_sec, speed_east_deg_sec; - double accN, accE, accD; - double rudder, elevator, left_aileron, right_aileron; - double time_stamp, last_time_stamp; - - SGPropertyNode_ptr _time_node; - - void Run(double dt); - inline double sign(double x) { return (x < 0.0) ? -1.0 : 1.0; } +public: + FGAIMultiplayer(); + virtual ~FGAIMultiplayer(); - string acType; - string company; + virtual bool init(); + virtual void bind(); + virtual void unbind(); + virtual void update(double dt); + + void addMotionInfo(const FGExternalMotionData& motionInfo, long stamp); + void setDoubleProperty(const std::string& prop, double val); + + void setCallSign(const string& callSign) + { mCallSign = callSign; } + const char* getCallSign(void) const + { return mCallSign.c_str(); } + + long getLastTimestamp(void) const + { return mLastTimestamp; } + + void setAllowExtrapolation(bool allowExtrapolation) + { mAllowExtrapolation = allowExtrapolation; } + bool getAllowExtrapolation(void) const + { return mAllowExtrapolation; } + void setLagAdjustSystemSpeed(double lagAdjustSystemSpeed) + { + if (lagAdjustSystemSpeed < 0) + lagAdjustSystemSpeed = 0; + mLagAdjustSystemSpeed = lagAdjustSystemSpeed; + } + double getLagAdjustSystemSpeed(void) const + { return mLagAdjustSystemSpeed; } + + void addPropertyId(unsigned id, const char* name) + { mPropertyMap[id] = props->getNode(name, true); } + + virtual const char* getTypeString(void) const { return "multiplayer"; } + +private: + + // Automatic sorting of motion data according to its timestamp + typedef std::map MotionInfo; + MotionInfo mMotionInfo; + + // Map between the property id's from the multiplayers network packets + // and the property nodes + typedef std::map > PropertyMap; + PropertyMap mPropertyMap; + + std::string mCallSign; + + double mTimeOffset; + bool mTimeOffsetSet; + + /// Properties which are for now exposed for testing + bool mAllowExtrapolation; + double mLagAdjustSystemSpeed; + + long mLastTimestamp; }; -inline void FGAIMultiplayer::setSpeedN(double sn) { speedN = sn; } -inline void FGAIMultiplayer::setSpeedE(double se) { speedE = se; } -inline void FGAIMultiplayer::setSpeedD(double sd) { speedD = sd; } -inline void FGAIMultiplayer::setAccN(double an) { accN = an; } -inline void FGAIMultiplayer::setAccE(double ae) { accE = ae; } -inline void FGAIMultiplayer::setAccD(double ad) { accD = ad; } -inline void FGAIMultiplayer::setRateH(double rh) { rateH = rh; } -inline void FGAIMultiplayer::setRateR(double rr) { rateR = rr; } -inline void FGAIMultiplayer::setRateP(double rp) { rateP = rp; } - #endif // _FG_AIMultiplayer_HXX diff --git a/src/Main/Makefile.am b/src/Main/Makefile.am index a09c7cd58..a619aa67d 100644 --- a/src/Main/Makefile.am +++ b/src/Main/Makefile.am @@ -1,11 +1,7 @@ EXTRA_DIST = 3dfx.sh runfgfs.in runfgfs.bat.in \ fg_os_sdl.cxx fg_os.cxx fg_os.hxx -if ENABLE_MPLAYER_AS MPLAYER_LIBS = $(top_builddir)/src/MultiPlayer/libMultiPlayer.a -else -MPLAYER_LIBS = -endif if ENABLE_SP_FDM SP_FDM_LIBS = $(top_builddir)/src/FDM/SP/libSPFDM.a diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 475460ede..491dd1edf 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -111,10 +111,7 @@ #include