45 #include "models/FGGroundReactions.h"
46 #include "math/FGTable.h"
47 #include "input_output/FGXMLElement.h"
48 #include "models/FGInertial.h"
64 const FGMatrix33 FGLGear::Tb2s(-1./inchtoft, 0., 0., 0., 1./inchtoft, 0., 0., 0., -1./inchtoft);
65 const FGMatrix33 FGLGear::Ts2b(-inchtoft, 0., 0., 0., inchtoft, 0., 0., 0., -inchtoft);
78 StaticFriction(false),
81 kSpring = bDamp = bDampRebound = dynamicFCoeff = staticFCoeff = rollingFCoeff = maxSteerAngle = 0;
82 isRetractable =
false;
84 eDampTypeRebound = dtLinear;
88 if (sContactType ==
"BOGEY") {
89 eContactType = ctBOGEY;
90 }
else if (sContactType ==
"STRUCTURE") {
91 eContactType = ctSTRUCTURE;
94 eContactType = ctSTRUCTURE;
98 if (eContactType == ctSTRUCTURE) {
99 kSpring = in.EmptyWeight;
101 bDampRebound = kSpring * 10;
112 fStrutForce =
new FGFunction(fdmex, springFunc);
120 eDampType = dtSquare;
130 eDampTypeRebound = dtSquare;
136 bDampRebound = bDamp;
137 eDampTypeRebound = eDampType;
155 if ((maxSteerAngle == 360 && !castered_el)
157 eSteerType = stCaster;
160 else if (maxSteerAngle == 0.0) {
161 eSteerType = stFixed;
164 eSteerType = stSteer;
170 while (force_table) {
172 if (force_type ==
"CORNERING_COEFF") {
173 ForceY_Table =
new FGTable(PropertyManager, force_table);
176 cerr <<
"Undefined force table for " << name <<
" contact point" << endl;
185 s <<
"No location given for contact " << name;
186 cerr << endl << s.str() << endl;
189 SetTransformType(FGForce::tCustom);
192 if (element && (eContactType == ctBOGEY)) {
195 mTGear = quatFromEuler.
GetT();
205 if (sBrakeGroup ==
"LEFT" ) eBrakeGrp = bgLeft;
206 else if (sBrakeGroup ==
"RIGHT" ) eBrakeGrp = bgRight;
207 else if (sBrakeGroup ==
"CENTER") eBrakeGrp = bgCenter;
208 else if (sBrakeGroup ==
"NOSE" ) eBrakeGrp = bgCenter;
209 else if (sBrakeGroup ==
"TAIL" ) eBrakeGrp = bgCenter;
210 else if (sBrakeGroup ==
"NONE" ) eBrakeGrp = bgNone;
211 else if (sBrakeGroup.empty() ) eBrakeGrp = bgNone;
213 cerr <<
"Improper braking group specification in config file: "
214 << sBrakeGroup <<
" is undefined." << endl;
220 useFCSGearPos =
false;
222 TakeoffReported = LandingReported =
false;
247 void FGLGear::ResetToIC(
void)
251 WOW = lastWOW =
false;
252 FirstContact =
false;
253 StartedGroundRun =
false;
254 LandingDistanceTraveled = TakeoffDistanceTraveled = TakeoffDistanceTraveled50ft = 0.0;
255 MaximumStrutForce = MaximumStrutTravel = 0.0;
256 SinkRate = GroundSpeed = 0.0;
259 vWhlVelVec.InitMatrix();
261 compressLength = 0.0;
268 for (
int i=0; i < 3; i++) {
269 LMultiplier[i].ForceJacobian.InitMatrix();
270 LMultiplier[i].LeverArm.InitMatrix();
271 LMultiplier[i].Min = 0.0;
272 LMultiplier[i].Max = 0.0;
273 LMultiplier[i].value = 0.0;
281 double gearPos = 1.0;
285 if (isRetractable) gearPos = GetGearUnitPos();
287 if (gearPos > 0.99) {
292 vLocalGear = in.Tb2l * vWhlBodyVec;
303 if (!fdmex->GetTrimStatus())
304 height -= (*surface).GetBumpHeight();
305 staticFFactor = (*surface).GetStaticFFactor();
306 rollingFFactor = (*surface).GetRollingFFactor();
307 maximumForce = (*surface).GetMaximumForce();
308 isSolid = (*surface).GetSolid();
312 double LGearProj = 1.0;
316 vGroundNormal = in.Tec2b * normal;
322 double normalZ = (in.Tec2l*normal)(eZ);
323 LGearProj = -(mTGear.
Transposed() * vGroundNormal)(eZ);
327 switch(eContactType) {
330 compressLength = LGearProj > 0.0 ? height * normalZ / LGearProj : 0.0;
338 compressLength = height * normalZ / DotProduct(normal, normal);
339 vWhlDisplVec = compressLength * vGroundNormal;
348 vActingXYZn = vXYZn + Tb2s * vWhlDisplVec;
350 vBodyWhlVel += in.UVW - in.Tec2b * terrainVel;
351 vWhlVelVec = mTGear.
Transposed() * vBodyWhlVel;
353 InitializeReporting();
354 ComputeSteeringAngle();
355 ComputeGroundFrame();
357 vGroundWhlVel = mT.
Transposed() * vBodyWhlVel;
359 if (fdmex->GetTrimStatus() || in.TotalDeltaT == 0.0)
362 compressSpeed = -vGroundWhlVel(eZ);
363 if (eContactType == ctBOGEY)
364 compressSpeed /= LGearProj;
369 double maxCompressSpeed = compressLength/in.TotalDeltaT;
370 if (fabs(compressSpeed) > maxCompressSpeed)
371 compressSpeed = sign(compressSpeed)*maxCompressSpeed;
374 ComputeVerticalStrutForce();
377 if (eContactType == ctBOGEY) {
379 ComputeBrakeForceCoefficient();
380 ComputeSideForceCoefficient();
385 ComputeJacobian(vWhlContactVec);
387 compressLength = 0.0;
391 vWhlDisplVec.InitMatrix();
393 LMultiplier[ftRoll].value = 0.0;
394 LMultiplier[ftSide].value = 0.0;
395 LMultiplier[ftDynamic].value = 0.0;
398 SteerAngle *= max(gearPos-0.8, 0.0)/0.2;
406 vWhlVelVec(eX) -= 13.0 * in.TotalDeltaT;
407 if (vWhlVelVec(eX) < 0.0) vWhlVelVec(eX) = 0.0;
410 if (!fdmex->GetTrimStatus()) {
411 ReportTakeoffOrLanding();
415 if (WOW && lastWOW) CrashDetect();
420 return FGForce::GetBodyForces();
429 void FGLGear::ComputeGroundFrame(
void)
434 roll -= DotProduct(roll, vGroundNormal) * vGroundNormal;
438 mT(eX,eX) = roll(eX);
439 mT(eY,eX) = roll(eY);
440 mT(eZ,eX) = roll(eZ);
441 mT(eX,eY) = side(eX);
442 mT(eY,eY) = side(eY);
443 mT(eZ,eY) = side(eZ);
444 mT(eX,eZ) = vGroundNormal(eX);
445 mT(eY,eZ) = vGroundNormal(eY);
446 mT(eZ,eZ) = vGroundNormal(eZ);
452 void FGLGear::ComputeSlipAngle(
void)
455 if (vGroundWhlVel.
Magnitude(eX,eY) > 1E-3)
456 WheelSlip = -atan2(vGroundWhlVel(eY), fabs(vGroundWhlVel(eX)))*radtodeg;
463 void FGLGear::ComputeSteeringAngle(
void)
468 SteerAngle = atan2(vWhlVelVec(eY), fabs(vWhlVelVec(eX)));
475 void FGLGear::ResetReporting(
void)
477 if (in.DistanceAGL > 200.0) {
478 FirstContact =
false;
479 StartedGroundRun =
false;
480 LandingReported =
false;
481 TakeoffReported =
true;
482 LandingDistanceTraveled = 0.0;
483 MaximumStrutForce = MaximumStrutTravel = 0.0;
489 void FGLGear::InitializeReporting(
void)
496 SinkRate = compressSpeed;
497 GroundSpeed = in.Vground;
498 TakeoffReported =
false;
503 if ((in.Vground > 0.1) &&
504 (in.BrakePos[bgLeft] == 0) &&
505 (in.BrakePos[bgRight] == 0) &&
506 (in.TakeoffThrottle && !StartedGroundRun))
508 TakeoffDistanceTraveled = 0;
509 TakeoffDistanceTraveled50ft = 0;
510 StartedGroundRun =
true;
517 void FGLGear::ReportTakeoffOrLanding(
void)
520 LandingDistanceTraveled += in.Vground * in.TotalDeltaT;
522 if (StartedGroundRun) {
523 TakeoffDistanceTraveled50ft += in.Vground * in.TotalDeltaT;
524 if (WOW) TakeoffDistanceTraveled += in.Vground * in.TotalDeltaT;
528 && in.Vground <= 0.05
532 if (debug_lvl > 0) Report(erLand);
537 && (in.DistanceAGL - vLocalGear(eZ)) > 50.0
540 if (debug_lvl > 0) Report(erTakeoff);
546 buf <<
"GEAR_CONTACT: " << fdmex->
GetSimTime() <<
" seconds: " << name;
554 void FGLGear::CrashDetect(
void)
556 if ( (compressLength > 500.0 ||
558 GetMoments().Magnitude() > 5000000000.0 ||
562 buf <<
"*CRASH DETECTED* " << fdmex->
GetSimTime() <<
" seconds: " << name;
576 void FGLGear::ComputeBrakeForceCoefficient(
void)
578 BrakeFCoeff = rollingFFactor * rollingFCoeff;
580 if (eBrakeGrp != bgNone)
581 BrakeFCoeff += in.BrakePos[eBrakeGrp] * staticFFactor * (staticFCoeff - rollingFCoeff);
594 void FGLGear::ComputeSideForceCoefficient(
void)
597 FCoeff = ForceY_Table->GetValue(WheelSlip);
599 double StiffSlip = Stiffness*WheelSlip;
600 FCoeff = Peak * sin(Shape*atan(StiffSlip - Curvature*(StiffSlip - atan(StiffSlip))));
602 FCoeff *= staticFFactor;
612 void FGLGear::ComputeVerticalStrutForce()
615 StrutForce = min(fStrutForce->
GetValue(), (
double)0.0);
617 double springForce = -compressLength * kSpring;
618 double dampForce = 0;
620 if (compressSpeed >= 0.0) {
622 if (eDampType == dtLinear)
623 dampForce = -compressSpeed * bDamp;
625 dampForce = -compressSpeed * compressSpeed * bDamp;
629 if (eDampTypeRebound == dtLinear)
630 dampForce = -compressSpeed * bDampRebound;
632 dampForce = compressSpeed * compressSpeed * bDampRebound;
636 StrutForce = min(springForce + dampForce, (
double)0.0);
637 if (StrutForce > maximumForce) {
638 StrutForce = maximumForce;
639 compressLength = -StrutForce / kSpring;
644 switch (eContactType) {
647 vFn(eZ) = StrutForce / (mTGear.
Transposed()*vGroundNormal)(eZ);
650 vFn(eZ) = -StrutForce;
655 MaximumStrutForce = max(MaximumStrutForce, fabs(StrutForce));
656 MaximumStrutTravel = max(MaximumStrutTravel, fabs(compressLength));
661 double FGLGear::GetGearUnitPos(
void)
const
664 if( useFCSGearPos || in.FCSGearPos != 1.0 ) {
665 useFCSGearPos =
true;
666 return in.FCSGearPos;
675 void FGLGear::ComputeJacobian(
const FGColumnVector3& vWhlContactVec)
680 if ((eContactType == ctSTRUCTURE) && (vGroundWhlVel.
Magnitude(eX,eY) > 1E-3)) {
682 FGColumnVector3 velocityDirection = vGroundWhlVel;
684 StaticFriction =
false;
686 velocityDirection(eZ) = 0.;
687 velocityDirection.Normalize();
689 LMultiplier[ftDynamic].ForceJacobian = mT * velocityDirection;
690 LMultiplier[ftDynamic].Max = 0.;
691 LMultiplier[ftDynamic].Min = -fabs(staticFFactor * dynamicFCoeff * vFn(eZ));
692 LMultiplier[ftDynamic].LeverArm = vWhlContactVec;
699 LMultiplier[ftDynamic].value =
Constrain(LMultiplier[ftDynamic].Min, LMultiplier[ftDynamic].value, LMultiplier[ftDynamic].Max);
701 GroundReactions->RegisterLagrangeMultiplier(&LMultiplier[ftDynamic]);
709 StaticFriction =
true;
711 LMultiplier[ftRoll].ForceJacobian = mT * FGColumnVector3(1.,0.,0.);
712 LMultiplier[ftSide].ForceJacobian = mT * FGColumnVector3(0.,1.,0.);
713 LMultiplier[ftRoll].LeverArm = vWhlContactVec;
714 LMultiplier[ftSide].LeverArm = vWhlContactVec;
716 switch(eContactType) {
718 LMultiplier[ftRoll].Max = fabs(BrakeFCoeff * vFn(eZ));
719 LMultiplier[ftSide].Max = fabs(FCoeff * vFn(eZ));
722 LMultiplier[ftRoll].Max = fabs(staticFFactor * staticFCoeff * vFn(eZ));
723 LMultiplier[ftSide].Max = LMultiplier[ftRoll].Max;
727 LMultiplier[ftRoll].Min = -LMultiplier[ftRoll].Max;
728 LMultiplier[ftSide].Min = -LMultiplier[ftSide].Max;
735 LMultiplier[ftRoll].value =
Constrain(LMultiplier[ftRoll].Min, LMultiplier[ftRoll].value, LMultiplier[ftRoll].Max);
736 LMultiplier[ftSide].value =
Constrain(LMultiplier[ftSide].Min, LMultiplier[ftSide].value, LMultiplier[ftSide].Max);
738 GroundReactions->RegisterLagrangeMultiplier(&LMultiplier[ftRoll]);
739 GroundReactions->RegisterLagrangeMultiplier(&LMultiplier[ftSide]);
747 void FGLGear::UpdateForces(
void)
749 if (StaticFriction) {
750 vFn(eX) = LMultiplier[ftRoll].value;
751 vFn(eY) = LMultiplier[ftSide].value;
754 FGColumnVector3 forceDir = mT.
Transposed() * LMultiplier[ftDynamic].ForceJacobian;
755 vFn(eX) = LMultiplier[ftDynamic].value * forceDir(eX);
756 vFn(eY) = LMultiplier[ftDynamic].value * forceDir(eY);
762 void FGLGear::SetstaticFCoeff(
double coeff)
764 staticFCoeff = coeff;
770 void FGLGear::bind(
void)
772 string property_name;
773 string base_property_name;
775 switch(eContactType) {
777 eSurfaceType = FGSurface::ctBOGEY;
778 base_property_name = CreateIndexedPropertyName(
"gear/unit", GearNumber);
781 eSurfaceType = FGSurface::ctSTRUCTURE;
782 base_property_name = CreateIndexedPropertyName(
"contact/unit", GearNumber);
789 property_name = base_property_name +
"/WOW";
790 PropertyManager->
Tie( property_name.c_str(), &WOW );
791 property_name = base_property_name +
"/x-position";
792 PropertyManager->
Tie( property_name.c_str(), (
FGForce*)
this,
793 &FGForce::GetLocationX, &FGForce::SetLocationX);
794 property_name = base_property_name +
"/y-position";
795 PropertyManager->
Tie( property_name.c_str(), (
FGForce*)
this,
796 &FGForce::GetLocationY, &FGForce::SetLocationY);
797 property_name = base_property_name +
"/z-position";
798 PropertyManager->
Tie( property_name.c_str(), (
FGForce*)
this,
799 &FGForce::GetLocationZ, &FGForce::SetLocationZ);
800 property_name = base_property_name +
"/compression-ft";
801 PropertyManager->
Tie( property_name.c_str(), &compressLength );
802 property_name = base_property_name +
"/compression-velocity-fps";
803 PropertyManager->
Tie( property_name.c_str(), &compressSpeed );
804 property_name = base_property_name +
"/static_friction_coeff";
805 PropertyManager->
Tie( property_name.c_str(), (
FGLGear*)
this,
806 &FGLGear::GetstaticFCoeff, &FGLGear::SetstaticFCoeff);
807 property_name = base_property_name +
"/dynamic_friction_coeff";
808 PropertyManager->
Tie( property_name.c_str(), &dynamicFCoeff );
810 if (eContactType == ctBOGEY) {
811 property_name = base_property_name +
"/slip-angle-deg";
812 PropertyManager->
Tie( property_name.c_str(), &WheelSlip );
813 property_name = base_property_name +
"/wheel-speed-fps";
814 PropertyManager->
Tie( property_name.c_str(), (
FGLGear*)
this,
815 &FGLGear::GetWheelRollVel);
816 property_name = base_property_name +
"/side_friction_coeff";
817 PropertyManager->
Tie( property_name.c_str(), &FCoeff );
818 property_name = base_property_name +
"/rolling_friction_coeff";
819 PropertyManager->
Tie( property_name.c_str(), &rollingFCoeff );
821 if (eSteerType == stCaster) {
822 property_name = base_property_name +
"/steering-angle-deg";
823 PropertyManager->
Tie( property_name.c_str(),
this, &FGLGear::GetSteerAngleDeg );
824 property_name = base_property_name +
"/castered";
825 PropertyManager->
Tie( property_name.c_str(), &Castered);
829 if( isRetractable ) {
830 property_name = base_property_name +
"/pos-norm";
831 PropertyManager->
Tie( property_name.c_str(), &GearPos );
834 if (eSteerType != stFixed) {
838 string tmp = CreateIndexedPropertyName(
"fcs/steer-pos-deg", GearNumber);
839 PropertyManager->
Tie(tmp.c_str(),
this, &FGLGear::GetSteerAngleDeg, &FGLGear::SetSteerAngleDeg);
845 void FGLGear::Report(ReportType repType)
847 if (fabs(TakeoffDistanceTraveled) < 0.001)
return;
851 cout << endl <<
"Touchdown report for " << name <<
" (WOW at time: "
852 << fdmex->
GetSimTime() <<
" seconds)" << endl;
853 cout <<
" Sink rate at contact: " << SinkRate <<
" fps, "
854 << SinkRate*0.3048 <<
" mps" << endl;
855 cout <<
" Contact ground speed: " << GroundSpeed*.5925 <<
" knots, "
856 << GroundSpeed*0.3048 <<
" mps" << endl;
857 cout <<
" Maximum contact force: " << MaximumStrutForce <<
" lbs, "
858 << MaximumStrutForce*4.448 <<
" Newtons" << endl;
859 cout <<
" Maximum strut travel: " << MaximumStrutTravel*12.0 <<
" inches, "
860 << MaximumStrutTravel*30.48 <<
" cm" << endl;
861 cout <<
" Distance traveled: " << LandingDistanceTraveled <<
" ft, "
862 << LandingDistanceTraveled*0.3048 <<
" meters" << endl;
863 LandingReported =
true;
866 cout << endl <<
"Takeoff report for " << name <<
" (Liftoff at time: "
867 << fdmex->
GetSimTime() <<
" seconds)" << endl;
868 cout <<
" Distance traveled: " << TakeoffDistanceTraveled
869 <<
" ft, " << TakeoffDistanceTraveled*0.3048 <<
" meters" << endl;
870 cout <<
" Distance traveled (over 50'): " << TakeoffDistanceTraveled50ft
871 <<
" ft, " << TakeoffDistanceTraveled50ft*0.3048 <<
" meters" << endl;
872 cout <<
" [Altitude (ASL): " << in.DistanceASL <<
" ft. / "
873 << in.DistanceASL*FGJSBBase::fttom <<
" m | Temperature: "
874 << in.Temperature - 459.67 <<
" F / "
876 cout <<
" [Velocity (KCAS): " << in.VcalibratedKts <<
"]" << endl;
877 TakeoffReported =
true;
903 void FGLGear::Debug(
int from)
905 static const char* sSteerType[] = {
"STEERABLE",
"FIXED",
"CASTERED" };
906 static const char* sBrakeGroup[] = {
"NONE",
"LEFT",
"RIGHT",
"CENTER",
"NOSE",
"TAIL"};
907 static const char* sContactType[] = {
"BOGEY",
"STRUCTURE" };
909 if (debug_lvl <= 0)
return;
913 cout <<
" " << sContactType[eContactType] <<
" " << name << endl;
914 cout <<
" Location: " << vXYZn << endl;
915 cout <<
" Spring Constant: " << kSpring << endl;
917 if (eDampType == dtLinear)
918 cout <<
" Damping Constant: " << bDamp <<
" (linear)" << endl;
920 cout <<
" Damping Constant: " << bDamp <<
" (square law)" << endl;
922 if (eDampTypeRebound == dtLinear)
923 cout <<
" Rebound Damping Constant: " << bDampRebound <<
" (linear)" << endl;
925 cout <<
" Rebound Damping Constant: " << bDampRebound <<
" (square law)" << endl;
927 cout <<
" Dynamic Friction: " << dynamicFCoeff << endl;
928 cout <<
" Static Friction: " << staticFCoeff << endl;
929 if (eContactType == ctBOGEY) {
930 cout <<
" Rolling Friction: " << rollingFCoeff << endl;
931 cout <<
" Steering Type: " << sSteerType[eSteerType] << endl;
932 cout <<
" Grouping: " << sBrakeGroup[eBrakeGrp] << endl;
933 cout <<
" Max Steer Angle: " << maxSteerAngle << endl;
934 cout <<
" Retractable: " << isRetractable << endl;
938 if (debug_lvl & 2 ) {
939 if (from == 0) cout <<
"Instantiated: FGLGear" << endl;
940 if (from == 1) cout <<
"Destroyed: FGLGear" << endl;
942 if (debug_lvl & 4 ) {
944 if (debug_lvl & 8 ) {
946 if (debug_lvl & 16) {
948 if (debug_lvl & 64) {