64const FGMatrix33 FGLGear::Tb2s(-1./inchtoft, 0., 0., 0., 1./inchtoft, 0., 0., 0., -1./inchtoft);
65const FGMatrix33 FGLGear::Ts2b(-inchtoft, 0., 0., 0., inchtoft, 0., 0., 0., -inchtoft);
78 StaticFriction(false),
82 isRetractable =
false;
88 if (sContactType ==
"BOGEY") {
90 }
else if (sContactType ==
"STRUCTURE") {
99 kSpring =
in.EmptyWeight;
101 bDampRebound = kSpring * 10;
106 PropertyManager =
fdmex->GetPropertyManager();
136 bDampRebound = bDamp;
137 eDampTypeRebound = eDampType;
155 if ((maxSteerAngle == 360 && !castered_el)
156 || (castered_el && castered_el->GetDataAsNumber() != 0.0)) {
157 eSteerType = stCaster;
160 else if (maxSteerAngle == 0.0) {
166 GroundReactions =
fdmex->GetGroundReactions();
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;
187 throw BaseException(s.str());
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;
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;
287 if (gearPos > 0.99) {
292 vLocalGear =
in.Tb2l * vWhlBodyVec;
293 gearLoc =
in.Location.LocalToLocation(vLocalGear);
297 double height =
fdmex->GetInertial()->GetContactPoint(gearLoc, contact,
303 if (!
fdmex->GetTrimStatus())
304 height -= (*surface).GetBumpHeight();
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;
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);
364 compressSpeed /= LGearProj;
369 double maxCompressSpeed = compressLength/
in.TotalDeltaT;
370 if (fabs(compressSpeed) > maxCompressSpeed)
371 compressSpeed =
sign(compressSpeed)*maxCompressSpeed;
374 ComputeVerticalStrutForce();
379 ComputeBrakeForceCoefficient();
380 ComputeSideForceCoefficient();
385 ComputeJacobian(vWhlContactVec);
387 compressLength = 0.0;
393 LMultiplier[
ftRoll].value = 0.0;
394 LMultiplier[
ftSide].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();
429void FGLGear::ComputeGroundFrame(
void)
434 roll -=
DotProduct(roll, vGroundNormal) * vGroundNormal;
452void FGLGear::ComputeSlipAngle(
void)
456 WheelSlip = -atan2(vGroundWhlVel(
eY), fabs(vGroundWhlVel(
eX)))*
radtodeg;
463void FGLGear::ComputeSteeringAngle(
void)
467 if (vWhlVelVec.Magnitude(
eX,
eY) > 0.1)
468 SteerAngle = atan2(vWhlVelVec(
eY), fabs(vWhlVelVec(
eX)));
475void 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;
489void FGLGear::InitializeReporting(
void)
496 SinkRate = compressSpeed;
497 GroundSpeed =
in.Vground;
498 TakeoffReported =
false;
503 if ((
in.Vground > 0.1) &&
506 (
in.TakeoffThrottle && !StartedGroundRun))
508 TakeoffDistanceTraveled = 0;
509 TakeoffDistanceTraveled50ft = 0;
510 StartedGroundRun =
true;
517void 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
537 && (
in.DistanceAGL - vLocalGear(
eZ)) > 50.0
546 buf <<
"GEAR_CONTACT: " <<
fdmex->GetSimTime() <<
" seconds: " << name;
554void FGLGear::CrashDetect(
void)
556 if ( (compressLength > 500.0 ||
557 vFn.Magnitude() > 100000000.0 ||
559 SinkRate > 1.4666*30 ) && !
fdmex->IntegrationSuspended())
562 buf <<
"*CRASH DETECTED* " <<
fdmex->GetSimTime() <<
" seconds: " << name;
576void FGLGear::ComputeBrakeForceCoefficient(
void)
594void 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))));
612void FGLGear::ComputeVerticalStrutForce()
615 StrutForce =
min(fStrutForce->GetValue(), (
double)0.0);
617 double springForce = -compressLength * kSpring;
618 double dampForce = 0;
620 if (compressSpeed >= 0.0) {
623 dampForce = -compressSpeed * bDamp;
625 dampForce = -compressSpeed * compressSpeed * bDamp;
630 dampForce = -compressSpeed * bDampRebound;
632 dampForce = compressSpeed * compressSpeed * bDampRebound;
636 StrutForce =
min(springForce + dampForce, (
double)0.0);
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));
664 if( useFCSGearPos ||
in.FCSGearPos != 1.0 ) {
665 useFCSGearPos =
true;
666 return in.FCSGearPos;
684 StaticFriction =
false;
686 velocityDirection(
eZ) = 0.;
709 StaticFriction =
true;
716 switch(eContactType) {
738 GroundReactions->RegisterLagrangeMultiplier(&LMultiplier[
ftRoll]);
739 GroundReactions->RegisterLagrangeMultiplier(&LMultiplier[
ftSide]);
747void FGLGear::UpdateForces(
void)
749 if (StaticFriction) {
754 FGColumnVector3 forceDir =
mT.Transposed() * LMultiplier[
ftDynamic].ForceJacobian;
762void FGLGear::SetstaticFCoeff(
double coeff)
772 string property_name;
773 string base_property_name;
775 switch(eContactType) {
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,
794 property_name = base_property_name +
"/y-position";
795 PropertyManager->Tie( property_name.c_str(), (
FGForce*)
this,
797 property_name = base_property_name +
"/z-position";
798 PropertyManager->Tie( property_name.c_str(), (
FGForce*)
this,
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,
807 property_name = base_property_name +
"/dynamic_friction_coeff";
808 PropertyManager->Tie( property_name.c_str(), &
dynamicFCoeff );
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,
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 );
822 property_name = base_property_name +
"/steering-angle-deg";
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 );
845void FGLGear::Report(ReportType repType)
847 if (fabs(TakeoffDistanceTraveled) < 0.001)
return;
851 cout << endl <<
"Touchdown report for " <<
name <<
" (WOW at time: "
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: "
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;
877 TakeoffReported =
true;
903void 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" };
913 cout <<
" " << sContactType[eContactType] <<
" " << name << endl;
914 cout <<
" Location: " <<
vXYZn << endl;
915 cout <<
" Spring Constant: " << kSpring << endl;
918 cout <<
" Damping Constant: " << bDamp <<
" (linear)" << endl;
920 cout <<
" Damping Constant: " << bDamp <<
" (square law)" << endl;
923 cout <<
" Rebound Damping Constant: " << bDampRebound <<
" (linear)" << endl;
925 cout <<
" Rebound Damping Constant: " << bDampRebound <<
" (square law)" << endl;
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;
939 if (from == 0) cout <<
"Instantiated: FGLGear" << endl;
940 if (from == 1) cout <<
"Destroyed: FGLGear" << endl;
double FindElementValueAsNumberConvertTo(const std::string &el, const std::string &target_units)
Searches for the named element and converts and returns the data belonging to it.
double FindElementValueAsNumber(const std::string &el="")
Searches for the named element and returns the data belonging to it as a number.
FGColumnVector3 FindElementTripletConvertTo(const std::string &target_units)
Composes a 3-element column vector for the supplied location or orientation.
std::string FindElementValue(const std::string &el="")
Searches for the named element and returns the string data belonging to it.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
Element * FindElement(const std::string &el="")
Searches for a specified element.
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
This class implements a 3 element column vector.
FGColumnVector3 & Normalize(void)
Normalize.
double Magnitude(void) const
Length of the vector.
double GetSimTime(void) const
Returns the cumulative simulation time in seconds.
void SetTransformType(TransformType ii)
virtual const FGColumnVector3 & GetBodyForces(void)
double GetLocationZ(void) const
void SetLocationY(double y)
double GetLocationY(void) const
void SetLocationX(double x)
void SetLocationZ(double z)
FGForce(FGFDMExec *FDMExec)
Constructor.
FGColumnVector3 vActingXYZn
double GetLocationX(void) const
const FGColumnVector3 & GetMoments(void) const
Represents a mathematical function.
void PutMessage(const Message &msg)
Places a Message structure on the Message queue.
static constexpr double radtodeg
static constexpr double Constrain(double min, double value, double max)
Constrain a value between a minimum and a maximum value.
static constexpr double sign(double num)
static constexpr double fttom
static std::string CreateIndexedPropertyName(const std::string &Property, int index)
static constexpr double RankineToCelsius(double rankine)
Converts from degrees Rankine to degrees Celsius.
void SetSteerAngleDeg(double angle)
double GetWheelRollVel(void) const
double GetstaticFCoeff(void) const
double GetGearUnitPos(void) const
double GetSteerAngleDeg(void) const
FGLGear(Element *el, FGFDMExec *Executive, int number, const struct Inputs &input)
Constructor.
FGLocation holds an arbitrary location in the Earth centered Earth fixed reference frame (ECEF).
Handles matrix math operations.
Models the Quaternion representation of rotations.
const FGMatrix33 & GetT(void) const
Transformation matrix.
FGSurface(FGFDMExec *fdmex, int number=-1)
Constructor.
double DotProduct(const FGColumnVector3 &v1, const FGColumnVector3 &v2)
Dot product of two vectors Compute and return the euclidean dot (or scalar) product of two vectors v1...
FGColumnVector3 ForceJacobian