6#include "BodyEnvironment.hpp"
7#include "RigidBody.hpp"
10#include <simgear/constants.h>
19Hitch::Hitch(
const char *
name)
26 _pos[
i] = _force[
i] = _winchPos[
i] = _mp_lpos[
i]=_towEndForce[
i]=_mp_force[
i]=0;
28 _global_ground[
i] = 0;
29 _global_ground[2] = 1;
30 _global_ground[3] = -1e5;
36 _towElasticConstant=1e5;
37 _towBreakForce=100000;
41 _winchInitialTowLength=1000;
45 _winchMaxTowLength=1000;
48 _towEndIsConnectedToProperty=
false;
50 _nodeIsMultiplayer=
false;
51 _nodeIsAiAircraft=
false;
52 _forceIsCalculatedByMaster=
false;
55 _height_above_ground=0;
56 _winch_height_above_ground=0;
60 _displayed_len_lower_dist_message=
false;
63 _mpAutoConnectPeriod=0;
64 _timeToNextAutoConnectTry=0;
65 _timeToNextReConnectTry=10;
66 _speed_in_tow_direction=0;
68 _mp_last_reported_dist=0;
69 _mp_last_reported_v=0;
71 _mp_open_last_state=
false;
72 _timeLagCorrectedDist=0;
76#define TIE(x,v) _tiedProperties.Tie(_node->getNode(x, true),v)
77 TIE(
"tow/length", SGRawValuePointer<float>(&_towLength));
78 TIE(
"automatic-release-angle-deg",SGRawValuePointer<float>(&_autoReleaseAngle));
79 TIE(
"tow/elastic-constant",SGRawValuePointer<float>(&_towElasticConstant));
80 TIE(
"tow/weight-per-m-kg-m",SGRawValuePointer<float>(&_towWeightPerM));
81 TIE(
"tow/break-force",SGRawValuePointer<float>(&_towBreakForce));
82 TIE(
"winch/max-speed-m-s",SGRawValuePointer<float>(&_winchMaxSpeed));
83 TIE(
"winch/rel-speed",SGRawValuePointer<float>(&_winchRelSpeed));
84 TIE(
"winch/initial-tow-length-m",SGRawValuePointer<float>(&_winchInitialTowLength));
85 TIE(
"winch/min-tow-length-m",SGRawValuePointer<float>(&_winchMinTowLength));
86 TIE(
"winch/max-tow-length-m",SGRawValuePointer<float>(&_winchMaxTowLength));
87 TIE(
"winch/global-pos-x",SGRawValuePointer<double>(&_winchPos[0]));
88 TIE(
"winch/global-pos-y",SGRawValuePointer<double>(&_winchPos[1]));
89 TIE(
"winch/global-pos-z",SGRawValuePointer<double>(&_winchPos[2]));
90 TIE(
"winch/max-power",SGRawValuePointer<float>(&_winchPower));
91 TIE(
"winch/max-force",SGRawValuePointer<float>(&_winchMaxForce));
92 TIE(
"winch/actual-force",SGRawValuePointer<float>(&_winchActualForce));
93 TIE(
"tow/end-force-x",SGRawValuePointer<float>(&_reportTowEndForce[0]));
94 TIE(
"tow/end-force-y",SGRawValuePointer<float>(&_reportTowEndForce[1]));
95 TIE(
"tow/end-force-z",SGRawValuePointer<float>(&_reportTowEndForce[2]));
96 TIE(
"force",SGRawValuePointer<float>(&_forceMagnitude));
97 TIE(
"open",SGRawValuePointer<bool>(&_open));
98 TIE(
"force-is-calculated-by-other",SGRawValuePointer<bool>(&_forceIsCalculatedByMaster));
99 TIE(
"local-pos-x",SGRawValuePointer<float>(&_pos[0]));
100 TIE(
"local-pos-y",SGRawValuePointer<float>(&_pos[1]));
101 TIE(
"local-pos-z",SGRawValuePointer<float>(&_pos[2]));
102 TIE(
"tow/dist",SGRawValuePointer<float>(&_dist));
103 TIE(
"tow/dist-time-lag-corrected",SGRawValuePointer<float>(&_timeLagCorrectedDist));
104 TIE(
"tow/connected-to-property-node",SGRawValuePointer<bool>(&_towEndIsConnectedToProperty));
105 TIE(
"tow/connected-to-mp-node",SGRawValuePointer<bool>(&_nodeIsMultiplayer));
106 TIE(
"tow/connected-to-ai-node",SGRawValuePointer<bool>(&_nodeIsAiAircraft));
107 TIE(
"tow/connected-to-ai-or-mp-id",SGRawValuePointer<int>(&_nodeID));
108 TIE(
"debug/hitch-height-above-ground",SGRawValuePointer<float>(&_height_above_ground));
109 TIE(
"debug/tow-end-height-above-ground",SGRawValuePointer<float>(&_winch_height_above_ground));
110 TIE(
"debug/tow-rel-lo-pos",SGRawValuePointer<float>(&_loPosFrac));
111 TIE(
"debug/tow-lowest-pos-height",SGRawValuePointer<float>(&_lowest_tow_height));
112 TIE(
"is-slave",SGRawValuePointer<bool>(&_isSlave));
113 TIE(
"speed-in-tow-direction",SGRawValuePointer<float>(&_speed_in_tow_direction));
114 TIE(
"mp-auto-connect-period",SGRawValuePointer<float>(&_mpAutoConnectPeriod));
115 TIE(
"mp-time-lag",SGRawValuePointer<float>(&_mp_time_lag));
117 _node->setStringValue(
"tow/node",
"");
118 _node->setStringValue(
"tow/connected-to-ai-or-mp-callsign");
119 _node->setBoolValue(
"broken",
false);
125 _tiedProperties.Untie();
129void Hitch::setPosition(
float* position)
132 for(
i=0;
i<3;
i++) _pos[
i] = position[
i];
135void Hitch::setTowLength(
float length)
140void Hitch::setOpen(
bool isOpen)
148 if (isOpen==_last_wish)
154void Hitch::setTowElasticConstant(
float sc)
156 _towElasticConstant=sc;
159void Hitch::setTowBreakForce(
float bf)
164void Hitch::setWinchMaxForce(
float f)
169void Hitch::setTowWeightPerM(
float rw)
174void Hitch::setWinchMaxSpeed(
float mws)
179void Hitch::setWinchRelSpeed(
float rws)
184void Hitch::setWinchPosition(
double *winchPosition)
186 for (
int i=0;
i<3;
i++)
187 _winchPos[
i]=winchPosition[
i];
190void Hitch::setMpAutoConnectPeriod(
float dt)
192 _mpAutoConnectPeriod=dt;
195void Hitch::setForceIsCalculatedByOther(
bool b)
197 _forceIsCalculatedByMaster=b;
200std::string Hitch::getConnectedPropertyNode()
const
203 return _towEndNode->getDisplayName();
205 return std::string(
"");
208void Hitch::setConnectedPropertyNode(
const char *nodename)
213void Hitch::setWinchPositionAuto(
bool doit)
215 static bool lastState=
false;
229 _state->planeGlobalToLocal(_global_ground, ground);
236 Math::cross3(ground,help,help);
240 Math::mul3((_winchInitialTowLength-1.),help,help);
242 Math::add3(_pos,help,lWinchPos);
244 Math::mul3(ground[3],ground,help);
245 Math::add3(lWinchPos,help,lWinchPos);
247 _state->posLocalToGlobal(lWinchPos,_winchPos);
248 _towLength=_winchInitialTowLength;
249 fgSetString(
"/sim/messages/pilot",
"Connected to winch!");
252 _node->setBoolValue(
"broken",
false);
256 Math::sub3(lWinchPos,_pos,delta);
257 _dist=Math::mag3(delta);
260void Hitch::findBestAIObject(
bool doit,
bool running_as_autoconnect)
262 static bool lastState=
false;
265 if (!running_as_autoconnect)
279 _state->posLocalToGlobal(_pos,gpos);
280 double bestdist=_towLength*_towLength;
281 _towEndIsConnectedToProperty=
false;
282 SGPropertyNode * ainode =
fgGetNode(
"/ai/models",
false);
284 std::string myCallsign =
"***********";
285 if (running_as_autoconnect)
288 SGPropertyNode *cs=
fgGetNode(
"/sim/multiplay/callsign",
false);
291 myCallsign = cs->getStringValue();
294 _towLength=_winchInitialTowLength;
297 vector <SGPropertyNode_ptr> nodes;
298 for (
int i=0;
i<ainode->nChildren();
i++)
300 SGPropertyNode * n=ainode->getChild(
i);
301 _nodeIsMultiplayer = strncmp(
"multiplayer", n->getNameString().c_str(), 11) == 0;
302 _nodeIsAiAircraft = strncmp(
"aircraft", n->getNameString().c_str(), 8) == 0;
303 if (!(_nodeIsMultiplayer || _nodeIsAiAircraft))
305 if (running_as_autoconnect)
307 if (!_nodeIsMultiplayer)
309 if(n->getBoolValue(
"sim/hitches/aerotow/open",
true))
continue;
310 if (myCallsign != n->getStringValue(
"sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign"))
314 pos[0]=n->getDoubleValue(
"position/global-x",0);
315 pos[1]=n->getDoubleValue(
"position/global-y",0);
316 pos[2]=n->getDoubleValue(
"position/global-z",0);
318 for (
int j=0;j<3;j++)
319 dist+=(pos[j]-gpos[j])*(pos[j]-gpos[j]);
324 _towEndIsConnectedToProperty=
true;
326 _node->setStringValue(
"tow/node",n->getDisplayName());
327 _nodeID=n->getIntValue(
"id",0);
328 _node->setStringValue(
"tow/connected-to-ai-or-mp-callsign",n->getStringValue(
"callsign"));
335 if (!running_as_autoconnect)
337 std::stringstream message;
338 message<<_node->getStringValue(
"tow/connected-to-ai-or-mp-callsign")
339 <<
", I am on your hook, distance "<<Math::sqrt(bestdist)<<
" meter.";
340 fgSetString(
"/sim/messages/pilot", message.str().c_str());
344 std::stringstream message;
345 message<<_node->getStringValue(
"tow/connected-to-ai-or-mp-callsign")
346 <<
": I am on your hook, distance "<<Math::sqrt(bestdist)<<
" meter.";
347 fgSetString(
"/sim/messages/ai-plane", message.str().c_str());
349 if (running_as_autoconnect)
353 _dist=_towLength*0.5;
354 _mp_open_last_state=
true;
357 if (!running_as_autoconnect)
359 fgSetString(
"/sim/messages/atc",
"Sorry, no aircraft for aerotow!");
363void Hitch::setWinchInitialTowLength(
float length)
365 _winchInitialTowLength=length;
368void Hitch::setWinchPower(
float power)
373void Hitch::setWinchMaxTowLength(
float length)
375 _winchMaxTowLength=length;
378void Hitch::setWinchMinTowLength(
float length)
380 _winchMinTowLength=length;
383void Hitch::setGlobalGround(
double *global_ground,
float *global_vel)
386 for(
i=0;
i<4;
i++) _global_ground[
i] = global_ground[
i];
387 for(
i=0;
i<3;
i++) _global_vel[
i] = global_vel[
i];
390void Hitch::getPosition(
float* out)
393 for(
i=0;
i<3;
i++) out[
i] = _pos[
i];
396float Hitch::getTowLength(
void)
401void Hitch::calcForce(Ground *g_cb, RigidBody* body, State* s)
403 float lWinchPos[3],delta[3],deltaN[3];
405 s->posGlobalToLocal(_winchPos,lWinchPos);
406 Math::sub3(lWinchPos,_pos,delta);
407 if(!_towEndIsConnectedToProperty)
408 _dist=Math::mag3(delta);
410 _dist=Math::mag3(lWinchPos);
413 Math::unit3(delta,deltaN);
415 s->globalToLocal(s->v,lvel);
416 _speed_in_tow_direction=Math::dot3(lvel,deltaN);
417 if (_towEndIsConnectedToProperty && _nodeIsMultiplayer)
419 float mp_delta_dist_due_to_time_lag=0.5*_mp_time_lag*(-_mp_v+_speed_in_tow_direction);
420 _timeLagCorrectedDist=_dist+mp_delta_dist_due_to_time_lag;
421 if(_forceIsCalculatedByMaster && !_open)
423 s->globalToLocal(_mp_force,_force);
428 _timeLagCorrectedDist=_dist;
431 _force[0]=_force[1]=_force[2]=0;
436 _forceMagnitude=(_dist-_towLength)/_towLength*_towElasticConstant;
438 _forceMagnitude=2*_towBreakForce;
441 if(_forceMagnitude>=_towBreakForce)
445 _node->setBoolValue(
"broken",
true);
446 _force[0]=_force[1]=_force[2]=0;
447 _towEndForce[0]=_towEndForce[1]=_towEndForce[2]=0;
448 _reportTowEndForce[0]=_reportTowEndForce[1]=_reportTowEndForce[2]=0;
451 Math::mul3(_forceMagnitude,deltaN,_force);
452 Math::mul3(-1.,_force,_towEndForce);
453 _winchActualForce=_forceMagnitude;
456 float grav_force=_towWeightPerM*_towLength*9.81;
458 float leng=_towLength+grav_force*_towLength/_towElasticConstant;
461 s->planeGlobalToLocal(_global_ground, ground);
467 _height_above_ground = ground[3] - Math::dot3(_pos, ground);
470 _winch_height_above_ground = ground[3] - Math::dot3(lWinchPos, ground);
473 float grav_frac=0.5*(1+(_height_above_ground-_winch_height_above_ground)/leng);
474 grav_frac=Math::clamp(grav_frac,0,1);
475 float grav_frac_tow_end=1-grav_frac;
477 if (_height_above_ground<leng)
479 float fa[3],fb[3],fg[3];
481 Math::mul3(-grav_frac*grav_force,ground,fg);
483 Math::add3(fg,_force,fa);
485 Math::mul3(-(1-grav_frac)*grav_force,ground,fg);
488 Math::sub3(fg,_force,fb);
489 float fa_=Math::mag3(fa);
490 float fb_=Math::mag3(fb);
491 float stretchedTowLen;
492 stretchedTowLen=_towLength*(1.+(fa_+fb_)/(2*_towElasticConstant));
495 _loPosFrac=fa_/(fa_+fb_);
502 Math::cross3(lWinchPos,ground,help);
503 ground_dist=Math::mag3(help);
505 _lowest_tow_height=_loPosFrac*Math::sqrt(Math::abs(stretchedTowLen*stretchedTowLen-ground_dist*ground_dist));
506 if (_height_above_ground<_lowest_tow_height)
508 if (_height_above_ground>1e-3)
509 grav_frac*=_height_above_ground/_lowest_tow_height;
513 if (_winch_height_above_ground<(_lowest_tow_height-_height_above_ground+_winch_height_above_ground))
515 if (_winch_height_above_ground>1e-3)
516 grav_frac_tow_end*=_winch_height_above_ground/
517 (_lowest_tow_height-_height_above_ground+_winch_height_above_ground);
522 else _lowest_tow_height=_loPosFrac=-1;
523 float grav_force_v[3];
524 Math::mul3(grav_frac*grav_force,ground,grav_force_v);
525 Math::add3(grav_force_v,_force,_force);
526 _forceMagnitude=Math::mag3(_force);
528 Math::mul3(grav_frac_tow_end*grav_force,ground,grav_force_v);
529 Math::add3(grav_force_v,_towEndForce,_towEndForce);
530 s->localToGlobal(_towEndForce,_towEndForce);
532 if(_forceMagnitude>=_towBreakForce)
536 _node->setBoolValue(
"broken",
true);
537 _force[0]=_force[1]=_force[2]=0;
538 _towEndForce[0]=_towEndForce[1]=_towEndForce[2]=0;
545void Hitch::getForce(
float* force,
float* off)
547 Math::set3(_force, force);
548 Math::set3(_pos, off);
551float Hitch::getRopeAngleDeg()
553 return -atan2(_force[2] , _force[0] ) * SG_RADIANS_TO_DEGREES;
556void Hitch::integrate (
float dt)
559 if (_open !=_oldOpen)
563 if (_dist>_towLength*1.00001)
565 std::stringstream message;
566 message<<
"Could not lock hitch (tow length is insufficient) on hitch "
567 <<_node->getNameString()<<
" "<<_node->getIndex()<<
"!";
568 fgSetString(
"/sim/messages/pilot", message.str().c_str());
572 _node->setBoolValue(
"broken",
false);
574 std::stringstream message;
575 if (_node->getBoolValue(
"broken",
false)&&_open)
576 message<<
"Oh no, the tow is broken";
578 message<<(_open?
"Opened hitch ":
"Locked hitch ")<<_node->getNameString()<<
" "<<_node->getIndex()<<
"!";
579 fgSetString(
"/sim/messages/pilot", message.str().c_str());
584 if(_open && _mpAutoConnectPeriod)
587 _timeToNextAutoConnectTry-=dt;
588 if ((_timeToNextAutoConnectTry>_mpAutoConnectPeriod) || (_timeToNextAutoConnectTry<0))
590 _timeToNextAutoConnectTry=_mpAutoConnectPeriod;
592 findBestAIObject(
true,
true);
596 if(_towEndIsConnectedToProperty)
601 std::string towNode = _node->getStringValue(
"tow/node");
602 _towEndNode=
fgGetNode(
"ai/models")->getNode(towNode,
false);
605 if (_nodeIsMultiplayer || _nodeIsAiAircraft)
607 std::string MPcallsign =
"";
608 std::string MPc =_node->getStringValue(
"tow/connected-to-ai-or-mp-callsign");
614 && _towEndNode->getStringValue(
"callsign") != MPcallsign)
617 _timeToNextReConnectTry-=dt;
618 if((_timeToNextReConnectTry<0)||(_timeToNextReConnectTry>10))
620 _timeToNextReConnectTry=10;
621 SGPropertyNode * ainode =
fgGetNode(
"/ai/models",
false);
624 for (
int i=0;
i<ainode->nChildren();
i++)
626 SGPropertyNode * n=ainode->getChild(
i);
627 if (_nodeIsMultiplayer
628 ? strncmp(
"multiplayer", n->getNameString().c_str(), 11) == 0
629 : strncmp(
"aircraft", n->getNameString().c_str(), 8)
631 if (n->getStringValue(
"callsign") == MPcallsign)
635 _node->setStringValue(
"tow/node",n->getDisplayName());
645 _winchPos[0]=_towEndNode->getDoubleValue(
"position/global-x",_winchPos[0]);
646 _winchPos[1]=_towEndNode->getDoubleValue(
"position/global-y",_winchPos[1]);
647 _winchPos[2]=_towEndNode->getDoubleValue(
"position/global-z",_winchPos[2]);
648 _mp_lpos[0]=_towEndNode->getFloatValue(
"sim/hitches/aerotow/local-pos-x",0);
649 _mp_lpos[1]=_towEndNode->getFloatValue(
"sim/hitches/aerotow/local-pos-y",0);
650 _mp_lpos[2]=_towEndNode->getFloatValue(
"sim/hitches/aerotow/local-pos-z",0);
651 _mp_dist=_towEndNode->getFloatValue(
"sim/hitches/aerotow/tow/dist");
652 _mp_v=_towEndNode->getFloatValue(
"sim/hitches/aerotow/speed-in-tow-direction");
653 _mp_force[0]=_towEndNode->getFloatValue(
"sim/hitches/aerotow/tow/end-force-x",0);
654 _mp_force[1]=_towEndNode->getFloatValue(
"sim/hitches/aerotow/tow/end-force-y",0);
655 _mp_force[2]=_towEndNode->getFloatValue(
"sim/hitches/aerotow/tow/end-force-z",0);
659#define gf(a,b) a=_towEndNode->getFloatValue(b,a)
660#define gb(a,b) a=_towEndNode->getBoolValue(b,a)
661 gf(_towLength,
"sim/hitches/aerotow/tow/length");
662 gf(_towElasticConstant,
"sim/hitches/aerotow/tow/elastic-constant");
663 gf(_towWeightPerM,
"sim/hitches/aerotow/tow/weight-per-m-kg-m");
664 gf(_towBreakForce,
"sim/hitches/aerotow/break-force");
665 gb(_open,
"sim/hitches/aerotow/open");
666 gb(_mp_is_slave,
"sim/hitches/aerotow/is-slave");
669 if (_mp_is_slave) _isSlave=
false;
674 bool mp_open=_towEndNode->getBoolValue(
"sim/hitches/aerotow/open",_mp_open_last_state);
675 if (mp_open != _mp_open_last_state)
677 _mp_open_last_state=mp_open;
683 std::stringstream message;
684 message<<_node->getStringValue(
"tow/connected-to-ai-or-mp-callsign")
685 <<
": I have released the tow!";
686 fgSetString(
"/sim/messages/ai-plane", message.str().c_str());
692 if ((_mp_last_reported_dist!=_mp_dist)||(_mp_last_reported_v!=_mp_v))
694 _mp_last_reported_dist=_mp_dist;
695 _mp_last_reported_v=_mp_v;
696 float total_v=-_mp_v+_speed_in_tow_direction;
697 float abs_v=Math::abs(total_v);
700 float actual_time_lag_guess=(_mp_dist-_dist)/total_v;
702 if((actual_time_lag_guess>0)&&(actual_time_lag_guess<5))
704 float frac=abs_v*0.01;
705 if (frac>0.05) frac=0.05;
709 _mp_time_lag=(1-frac)*_mp_time_lag+frac*actual_time_lag_guess;
717 Math::set3(_towEndForce,_reportTowEndForce);
720 if (_winchRelSpeed==0)
return;
722 float forceMagnitude = Math::mag3(_towEndForce);
723 if (forceMagnitude > 100 && _autoReleaseAngle && (this->getRopeAngleDeg() > _autoReleaseAngle))
725 std::stringstream message;
726 message<<
"Cable autoreleased";
727 fgSetString(
"/sim/messages/pilot", message.str().c_str());
732 float factor=1,offset=0;
733 if (_winchActualForce>_winchMaxForce)
734 offset=-(_winchActualForce-_winchMaxForce)/_winchMaxForce*20;
735 if (_winchRelSpeed>0.01)
737 float maxForcePowerLimit=_winchPower/(_winchRelSpeed*_winchMaxSpeed);
738 if (_winchActualForce>maxForcePowerLimit)
739 factor-=(_winchActualForce-maxForcePowerLimit)/maxForcePowerLimit;
741 _towLength-=dt*(factor*_winchRelSpeed+offset)*_winchMaxSpeed;
742 if (_towLength<=_winchMinTowLength)
744 if (_winchRelSpeed<0)
746 _towLength=_winchMinTowLength;
749 if (_towLength>=_winchMaxTowLength)
751 if (_winchRelSpeed<0)
753 _towLength=_winchMaxTowLength;
State
Models an aircraft axis for purposes of trimming.
bool fgSetString(char const *name, char const *str)
Set a string value for a property.
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.