#endif
#include <string>
+#include <stdio.h>
+
+#include <Main/globals.hxx>
+#include <Main/fg_props.hxx>
#include "AIMultiplayer.hxx"
-#include <simgear/scene/util/SGNodeMasks.hxx>
+using std::string;
// #define SG_DEBUG SG_ALERT
-FGAIMultiplayer::FGAIMultiplayer() : FGAIBase(otMultiplayer) {
+FGAIMultiplayer::FGAIMultiplayer() :
+ FGAIBase(otMultiplayer, fgGetBool("/sim/multiplay/hot", false))
+{
no_roll = false;
mTimeOffsetSet = false;
mAllowExtrapolation = true;
mLagAdjustSystemSpeed = 10;
+ mLastTimestamp = 0;
+ lastUpdateTime = 0;
+ playerLag = 0.03;
+ compensateLag = 1;
- aip.getSceneGraph()->setNodeMask(~SG_NODEMASK_TERRAIN_BIT);
-
-}
-
+}
FGAIMultiplayer::~FGAIMultiplayer() {
}
isTanker = false; // do this until this property is
// passed over the net
- string str1 = _getCallsign();
- string str2 = "MOBIL";
+ const string& str1 = _getCallsign();
+ const string str2 = "MOBIL";
string::size_type loc1= str1.find( str2, 0 );
if ( (loc1 != string::npos && str2 != "") ){
void FGAIMultiplayer::bind() {
FGAIBase::bind();
- props->tie("refuel/contact", SGRawValuePointer<bool>(&contact));
- props->tie("tanker", SGRawValuePointer<bool>(&isTanker));
+ tie("refuel/contact", SGRawValuePointer<bool>(&contact));
+ tie("tanker", SGRawValuePointer<bool>(&isTanker));
- props->tie("controls/invisible",
+ tie("controls/invisible",
SGRawValuePointer<bool>(&invisible));
-
+ _uBodyNode = props->getNode("velocities/uBody-fps", true);
+ _vBodyNode = props->getNode("velocities/vBody-fps", true);
+ _wBodyNode = props->getNode("velocities/wBody-fps", true);
+
#define AIMPROProp(type, name) \
SGRawValueMethods<FGAIMultiplayer, type>(*this, &FGAIMultiplayer::get##name)
SGRawValueMethods<FGAIMultiplayer, type>(*this, \
&FGAIMultiplayer::get##name, &FGAIMultiplayer::set##name)
- //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));
+ //tie("callsign", AIMPROProp(const char *, CallSign));
+ tie("controls/allow-extrapolation",
+ AIMPRWProp(bool, AllowExtrapolation));
+ tie("controls/lag-adjust-system-speed",
+ AIMPRWProp(double, LagAdjustSystemSpeed));
+ tie("controls/player-lag",
+ AIMPRWProp(double, playerLag));
+ tie("controls/compensate-lag",
+ AIMPRWProp(int, compensateLag));
#undef AIMPROProp
#undef AIMPRWProp
}
-void FGAIMultiplayer::unbind() {
- FGAIBase::unbind();
-
- //props->untie("callsign");
- props->untie("controls/allow-extrapolation");
- props->untie("controls/lag-adjust-system-speed");
- props->untie("controls/invisible");
- props->untie("refuel/contact");
- props->untie("tanker");
-
-}
-
void FGAIMultiplayer::update(double dt)
{
using namespace simgear;
mTimeOffsetSet = true;
mTimeOffset = curentPkgTime - curtime - lag;
} else {
- double offset = curentPkgTime - curtime - lag;
+ double offset = 0.0;
+
+ //spectator mode, more late to be in the interpolation zone
+ if (compensateLag == 3) { offset = curentPkgTime -curtime -lag + playerLag;
+
+ // old behaviour
+ } else if (compensateLag == 1) { offset = curentPkgTime - curtime - lag;
+
+ // using the prediction mode to display the mpaircraft in the futur/past with given playerlag value
+ //currently compensatelag = 2
+ } else { offset = curentPkgTime - curtime + 0.48*lag + playerLag;
+ }
if ((!mAllowExtrapolation && offset + lag < mTimeOffset)
|| (offset - 10 > mTimeOffset)) {
mTimeOffset = offset;
- SG_LOG(SG_GENERAL, SG_DEBUG, "Resetting time offset adjust system to "
+ SG_LOG(SG_AI, 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
// 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);
- }
+
+ //trying to slow the rudderlag phenomenon thus using more the prediction system
+ //if we are off by less than 1.5s, do a little correction, and bigger step above 1.5s
+ if (fabs(err) < 1.5) {
+ if (err < 0) {
+ sysSpeed = mLagAdjustSystemSpeed*err*0.01;
+ } else {
+ sysSpeed = SGMiscd::min(0.5*err*err, 0.05);
+ }
+ } else {
+ 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
systemIncrement = err;
mTimeOffset += systemIncrement;
- SG_LOG(SG_GENERAL, SG_DEBUG, "Offset adjust system: time offset = "
+ SG_LOG(SG_AI, 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));
SGVec3d ecPos;
SGQuatf ecOrient;
+ SGVec3f ecLinearVel;
if (tInterp <= curentPkgTime) {
// Ok, we need a time prevous to the last available packet,
// 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!");
+ SG_LOG(SG_AI, 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;
- speed = norm(firstIt->second.linearVel) * SG_METER_TO_NM * 3600.0;
+ ecLinearVel = firstIt->second.linearVel;
+ speed = norm(ecLinearVel) * SG_METER_TO_NM * 3600.0;
std::vector<FGPropertyData*>::const_iterator firstPropIt;
std::vector<FGPropertyData*>::const_iterator firstPropItEnd;
}
else
{
- SG_LOG(SG_GENERAL, SG_DEBUG, "Unable to find property: " << (*firstPropIt)->id << "\n");
+ SG_LOG(SG_AI, SG_DEBUG, "Unable to find property: " << (*firstPropIt)->id << "\n");
}
++firstPropIt;
}
double intervalLen = intervalEnd - intervalStart;
double tau = (tInterp - intervalStart)/intervalLen;
- SG_LOG(SG_GENERAL, SG_DEBUG, "Multiplayer vehicle interpolation: ["
+ SG_LOG(SG_AI, SG_DEBUG, "Multiplayer vehicle interpolation: ["
<< intervalStart << ", " << intervalEnd << "], intervalLen = "
<< intervalLen << ", interpolation parameter = " << tau);
ecPos = ((1-tau)*prevIt->second.position + tau*nextIt->second.position);
ecOrient = interpolate((float)tau, prevIt->second.orientation,
nextIt->second.orientation);
- speed = norm((1-tau)*prevIt->second.linearVel
- + tau*nextIt->second.linearVel) * SG_METER_TO_NM * 3600.0;
+ ecLinearVel = ((1-tau)*prevIt->second.linearVel + tau*nextIt->second.linearVel);
+ speed = norm(ecLinearVel) * SG_METER_TO_NM * 3600.0;
if (prevIt->second.properties.size()
== nextIt->second.properties.size()) {
}
else
{
- SG_LOG(SG_GENERAL, SG_DEBUG, "Unable to find property: " << (*prevPropIt)->id << "\n");
+ SG_LOG(SG_AI, SG_DEBUG, "Unable to find property: " << (*prevPropIt)->id << "\n");
}
++prevPropIt;
// and do some eom computation to guess that for now.
FGExternalMotionData& motionInfo = it->second;
- // The time to predict, limit to 5 seconds
+ // The time to predict, limit to 3 seconds
double t = tInterp - motionInfo.time;
- t = SGMisc<double>::min(t, 5);
+ t = SGMisc<double>::min(t, 3);
- SG_LOG(SG_GENERAL, SG_DEBUG, "Multiplayer vehicle extrapolation: "
+ SG_LOG(SG_AI, SG_DEBUG, "Multiplayer vehicle extrapolation: "
"extrapolation time = " << t);
- // Do a few explicit euler steps with the constant acceleration's
- // This must be sufficient ...
+ // using velocity and acceleration to guess a parabolic position...
ecPos = motionInfo.position;
ecOrient = motionInfo.orientation;
- SGVec3f linearVel = motionInfo.linearVel;
+ ecLinearVel = motionInfo.linearVel;
+ SGVec3d ecVel = toVec3d(ecOrient.backTransform(ecLinearVel));
SGVec3f angularVel = motionInfo.angularVel;
- while (0 < t) {
- double h = 1e-1;
- if (t < h)
- h = t;
+ SGVec3d ecAcc = toVec3d(ecOrient.backTransform(motionInfo.linearAccel));
- SGVec3d ecVel = toVec3d(ecOrient.backTransform(linearVel));
- ecPos += h*ecVel;
- ecOrient += h*ecOrient.derivative(angularVel);
+ double normVel = norm(ecVel);
- linearVel += h*(cross(linearVel, angularVel) + motionInfo.linearAccel);
- angularVel += h*motionInfo.angularAccel;
-
- t -= h;
- }
+ // not doing rotationnal prediction for small speed or rotation rate,
+ // to avoid agitated parked plane
+ if (( norm(angularVel) > 0.05 ) || ( normVel > 1.0 )) {
+ ecOrient += t*ecOrient.derivative(angularVel);
+ }
+
+ // not using acceleration for small speed, to have better parked planes
+ // note that anyway acceleration is not transmit yet by mp
+ if ( normVel > 1.0 ) {
+ ecPos += t*(ecVel + 0.5*t*ecAcc);
+ } else {
+ ecPos += t*(ecVel);
+ }
- std::vector<FGPropertyData*>::const_iterator firstPropIt;
+ std::vector<FGPropertyData*>::const_iterator firstPropIt;
std::vector<FGPropertyData*>::const_iterator firstPropItEnd;
- speed = norm(linearVel) * SG_METER_TO_NM * 3600.0;
+ speed = norm(ecLinearVel) * SG_METER_TO_NM * 3600.0;
firstPropIt = it->second.properties.begin();
firstPropItEnd = it->second.properties.end();
while (firstPropIt != firstPropItEnd) {
}
else
{
- SG_LOG(SG_GENERAL, SG_DEBUG, "Unable to find property: " << (*firstPropIt)->id << "\n");
+ SG_LOG(SG_AI, SG_DEBUG, "Unable to find property: " << (*firstPropIt)->id << "\n");
}
++firstPropIt;
// extract the position
pos = SGGeod::fromCart(ecPos);
+ double recent_alt_ft = altitude_ft;
altitude_ft = pos.getElevationFt();
+ // expose a valid vertical speed
+ if (lastUpdateTime != 0)
+ {
+ double dT = curtime - lastUpdateTime;
+ double Weighting=1;
+ if (dt < 1.0)
+ Weighting = dt;
+ // simple smoothing over 1 second
+ vs = (1.0-Weighting)*vs + Weighting * (altitude_ft - recent_alt_ft) / dT * 60;
+ }
+ lastUpdateTime = curtime;
+
// The quaternion rotating from the earth centered frame to the
// horizontal local frame
SGQuatf qEc2Hl = SGQuatf::fromLonLatRad((float)pos.getLongitudeRad(),
roll = rDeg;
pitch = pDeg;
- SG_LOG(SG_GENERAL, SG_DEBUG, "Multiplayer position and orientation: "
+ // expose velocities/u,v,wbody-fps in the mp tree
+ _uBodyNode->setValue(ecLinearVel[0] * SG_METER_TO_FEET);
+ _vBodyNode->setValue(ecLinearVel[1] * SG_METER_TO_FEET);
+ _wBodyNode->setValue(ecLinearVel[2] * SG_METER_TO_FEET);
+
+ SG_LOG(SG_AI, SG_DEBUG, "Multiplayer position and orientation: "
<< ecPos << ", " << hlOr);
//###########################//