Module: FGLGear.cpp
Author: Jon S. Berndt
Norman H. Princen
+ Bertrand Coconnier
Date started: 11/18/99
Purpose: Encapsulates the landing gear elements
Called by: FGAircraft
--------------------------------------------------------------------------------
11/18/99 JSB Created
01/30/01 NHP Extended gear model to properly simulate steering and braking
+07/08/09 BC Modified gear model to support large angles between aircraft and ground
/%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
TakeoffReported = LandingReported = false;
LandingDistanceTraveled = TakeoffDistanceTraveled = TakeoffDistanceTraveled50ft = 0.0;
MaximumStrutForce = MaximumStrutTravel = 0.0;
- SideForce = RollingForce = 0.0;
SinkRate = GroundSpeed = 0.0;
vWhlBodyVec = MassBalance->StructuralToBody(vXYZ);
vLocalGear = Propagate->GetTb2l() * vWhlBodyVec;
+ vLocalWhlVel.InitMatrix();
+
compressLength = 0.0;
compressSpeed = 0.0;
brakePct = 0.0;
WheelSlip = 0.0;
TirePressureNorm = 1.0;
- SideWhlVel = 0.0;
- RollingWhlVel = 0.0;
-
- SinWheel = 0.0;
- CosWheel = 0.0;
-
// Set Pacejka terms
Stiffness = 0.06;
dT = State->Getdt()*Exec->GetGroundReactions()->GetRate();
vForce.InitMatrix();
+ vLocalForce.InitMatrix();
vMoment.InitMatrix();
if (isRetractable) ComputeRetractionState();
vLocalGear = Propagate->GetTb2l() * vWhlBodyVec; // Get local frame wheel location
gearLoc = Propagate->GetLocation().LocalToLocation(vLocalGear);
- compressLength = -Exec->GetGroundCallback()->GetAGLevel(t, gearLoc, contact, normal, cvel);
-
- // The compression length is measured in the Z-axis, only, at this time.
+ // Compute the height of the theoritical location of the wheel (if struct was not compressed) with
+ // respect to the ground level
+ double height = Exec->GetGroundCallback()->GetAGLevel(t, gearLoc, contact, normal, cvel);
+ vGroundNormal = -1. * Propagate->GetTec2b() * normal;
+
+ switch (eContactType) {
+ case ctBOGEY:
+ // Project the height in the local coordinate frame of the strut to compute the actual compression
+ // length. The strut is assumed to be parallel to Z in the body frame.
+ compressLength = vGroundNormal(eZ) < 0.0 ? height / vGroundNormal(eZ) : 0.0;
+ break;
+ case ctSTRUCTURE:
+ compressLength = -height;
+ break;
+ }
if (compressLength > 0.00) {
// [The next equation should really use the vector to the contact patch of
// the tire including the strut compression and not the original vWhlBodyVec.]
- vWhlVelVec = Propagate->GetTb2l() * (Propagate->GetPQR() * vWhlBodyVec);
- vWhlVelVec += Propagate->GetVel() - cvel;
- compressSpeed = vWhlVelVec(eZ);
+ FGColumnVector3 vWhlContactVec = vWhlBodyVec - FGColumnVector3(0., 0., compressLength);
+ vWhlVelVec = Propagate->GetPQR() * vWhlContactVec;
+ vWhlVelVec += Propagate->GetUVW() - Propagate->GetTec2b() * cvel;
InitializeReporting();
- ComputeBrakeForceCoefficient();
ComputeSteeringAngle();
- ComputeSlipAngle();
- ComputeSideForceCoefficient();
+ ComputeGroundCoordSys();
+
+ vLocalWhlVel = Tb2g * vWhlVelVec;
+
+ compressSpeed = -vLocalWhlVel(eZ);
+ if (eContactType == ctBOGEY)
+ // Project the compression speed in the local coordinate frame of the strut
+ compressSpeed /= -vGroundNormal(eZ);
+
ComputeVerticalStrutForce();
// Compute the forces in the wheel ground plane.
-
- double sign = RollingWhlVel>0?1.0:(RollingWhlVel<0?-1.0:0.0);
- RollingForce = ((1.0 - TirePressureNorm) * 30 + vLocalForce(eZ) * BrakeFCoeff) * sign;
- SideForce = vLocalForce(eZ) * FCoeff;
+ if (eContactType == ctBOGEY) {
+ ComputeSlipAngle();
+ ComputeBrakeForceCoefficient();
+ ComputeSideForceCoefficient();
+ double sign = vLocalWhlVel(eX)>0?1.0:(vLocalWhlVel(eX)<0?-1.0:0.0);
+ vLocalForce(eX) = - ((1.0 - TirePressureNorm) * 30 + vLocalForce(eZ) * BrakeFCoeff) * sign;
+ vLocalForce(eY) = vLocalForce(eZ) * FCoeff;
+ }
+ else if (eContactType == ctSTRUCTURE) {
+ FGColumnVector3 vSlipVec = vLocalWhlVel;
+ vSlipVec(eZ) = 0.;
+ vSlipVec.Normalize();
+ vLocalForce -= staticFCoeff * vLocalForce(eZ) * vSlipVec;
+ }
// Lag and attenuate the XY-plane forces dependent on velocity. This code
// uses a lag filter, C/(s + C) where "C" is the filter coefficient. When
// If a coefficient is set to something equal to or less than zero, the
// filter is bypassed.
- if (LongForceLagFilterCoeff > 0) RollingForce = LongForceFilter.execute(RollingForce);
- if (LatForceLagFilterCoeff > 0) SideForce = LatForceFilter.execute(SideForce);
+ if (LongForceLagFilterCoeff > 0) vLocalForce(eX) = LongForceFilter.execute(vLocalForce(eX));
+ if (LatForceLagFilterCoeff > 0) vLocalForce(eY) = LatForceFilter.execute(vLocalForce(eY));
- if ((fabs(RollingWhlVel) <= RFRV) && RFRV > 0) RollingForce *= fabs(RollingWhlVel)/RFRV;
- if ((fabs(SideWhlVel) <= SFRV) && SFRV > 0) SideForce *= fabs(SideWhlVel)/SFRV;
+ if ((fabs(vLocalWhlVel(eX)) <= RFRV) && RFRV > 0) vLocalForce(eX) *= fabs(vLocalWhlVel(eX))/RFRV;
+ if ((fabs(vLocalWhlVel(eY)) <= SFRV) && SFRV > 0) vLocalForce(eY) *= fabs(vLocalWhlVel(eY))/SFRV;
- // Transform these forces back to the local reference frame.
-
- vLocalForce(eX) = RollingForce*CosWheel - SideForce*SinWheel;
- vLocalForce(eY) = SideForce*CosWheel + RollingForce*SinWheel;
+ // End section for attenuating gear jitter
// Transform the forces back to the body frame and compute the moment.
- vForce = Propagate->GetTl2b() * vLocalForce;
-
- // End section for attentuating gear jitter
-
- vMoment = vWhlBodyVec * vForce;
+ vForce = Tg2b * vLocalForce;
+ vMoment = vWhlContactVec * vForce;
} else { // Gear is NOT compressed
compressLength = 0.0;
compressSpeed = 0.0;
- // No wheel conditions
- SideWhlVel = WheelSlip = 0.0;
-
// Let wheel spin down slowly
- RollingWhlVel -= 13.0*dT;
- if (RollingWhlVel < 0.0) RollingWhlVel = 0.0;
+ vLocalWhlVel(eX) -= 13.0*dT;
+ if (vLocalWhlVel(eX) < 0.0) vLocalWhlVel(eX) = 0.0;
// Return to neutral position between 1.0 and 0.8 gear pos.
SteerAngle *= max(GetGearUnitPos()-0.8, 0.0)/0.2;
return vForce;
}
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Build a local "ground" coordinate system defined by
+// eX : projection of the rolling direction on the ground
+// eY : projection of the sliping direction on the ground
+// eZ : normal to the ground
+
+void FGLGear::ComputeGroundCoordSys(void)
+{
+ // Compute the rolling direction projected on the ground
+ // It consists in finding a vector 'r' such that 'r' lies in the plane (w,z) and r.n = 0 (scalar
+ // product) where:
+ // 'n' is the normal to the ground,
+ // (x,y,z) are the directions defined in the body coord system
+ // and 'w' is 'x' rotated by the steering angle (SteerAngle) in the plane (x,y).
+ // r = u * w + v * z and r.n = 0 => v/u = -w.n/z.n = a
+ // We also want u**2+v**2=1 and u > 0 (i.e. r orientated in the same 'direction' than w)
+ // after some arithmetic, one finds that :
+ double a = -(vGroundNormal(eX)*cos(SteerAngle)+vGroundNormal(eY)*sin(SteerAngle)) / vGroundNormal(eZ);
+ double u = 1. / sqrt(1. + a*a);
+ double v = a * u;
+ FGColumnVector3 vRollingGroundVec = FGColumnVector3(u * cos(SteerAngle), u * sin(SteerAngle), v);
+
+ // The sliping direction is the cross product multiplication of the ground normal and rolling
+ // directions
+ FGColumnVector3 vSlipGroundVec = vGroundNormal * vRollingGroundVec;
+
+ Tg2b(1,1) = vRollingGroundVec(eX);
+ Tg2b(2,1) = vRollingGroundVec(eY);
+ Tg2b(3,1) = vRollingGroundVec(eZ);
+ Tg2b(1,2) = vSlipGroundVec(eX);
+ Tg2b(2,2) = vSlipGroundVec(eY);
+ Tg2b(3,2) = vSlipGroundVec(eZ);
+ Tg2b(1,3) = vGroundNormal(eX);
+ Tg2b(2,3) = vGroundNormal(eY);
+ Tg2b(3,3) = vGroundNormal(eZ);
+
+ Tb2g = Tg2b.Transposed();
+}
+
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGLGear::ComputeRetractionState(void)
GearUp = true;
WOW = false;
GearDown = false;
- RollingWhlVel = 0.0;
+ vLocalWhlVel.InitMatrix();
} else if (gearPos > 0.99) {
GearDown = true;
GearUp = false;
void FGLGear::ComputeSlipAngle(void)
{
- // Transform the wheel velocities from the local axis system to the wheel axis system.
- RollingWhlVel = vWhlVelVec(eX)*CosWheel + vWhlVelVec(eY)*SinWheel;
- SideWhlVel = vWhlVelVec(eY)*CosWheel - vWhlVelVec(eX)*SinWheel;
-
// Calculate tire slip angle.
- WheelSlip = atan2(SideWhlVel, fabs(RollingWhlVel))*radtodeg;
+ WheelSlip = -atan2(vLocalWhlVel(eY), fabs(vLocalWhlVel(eX)))*radtodeg;
// Filter the wheel slip angle
if (WheelSlipLagFilterCoeff > 0) WheelSlip = WheelSlipFilter.execute(WheelSlip);
void FGLGear::ComputeSteeringAngle(void)
{
- double casterLocalFrameAngleRad = 0.0;
- double casterAngle = 0.0;
-
switch (eSteerType) {
case stSteer:
SteerAngle = degtorad * FCS->GetSteerPosDeg(GearNumber);
SteerAngle = 0.0;
break;
case stCaster:
- // This is not correct for castering gear. Should make steer angle parallel
- // to the actual velocity vector of the wheel, given aircraft velocity vector
- // and omega.
- SteerAngle = 0.0;
- casterLocalFrameAngleRad = acos(vWhlVelVec(eX)/vWhlVelVec.Magnitude());
- casterAngle = casterLocalFrameAngleRad - Propagate->GetEuler(ePsi);
+ SteerAngle = atan2(fabs(vWhlVelVec(eX)), vWhlVelVec(eY));
break;
default:
cerr << "Improper steering type membership detected for this gear." << endl;
break;
}
-
- SinWheel = sin(Propagate->GetEuler(ePsi) + SteerAngle);
- CosWheel = cos(Propagate->GetEuler(ePsi) + SteerAngle);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
dampForce = compressSpeed * compressSpeed * bDampRebound;
}
- vLocalForce(eZ) = min(springForce + dampForce, (double)0.0);
+
+ StrutForce = min(springForce + dampForce, (double)0.0);
+
+ // The reaction force of the wheel is always normal to the ground
+ switch (eContactType) {
+ case ctBOGEY:
+ // Project back the strut force in the local coordinate frame of the ground
+ vLocalForce(eZ) = StrutForce / vGroundNormal(eZ);
+ break;
+ case ctSTRUCTURE:
+ vLocalForce(eZ) = -StrutForce;
+ break;
+ }
// Remember these values for reporting
- MaximumStrutForce = max(MaximumStrutForce, fabs(vLocalForce(eZ)));
+ MaximumStrutForce = max(MaximumStrutForce, fabs(StrutForce));
MaximumStrutTravel = max(MaximumStrutTravel, fabs(compressLength));
}
property_name = base_property_name + "/WOW";
Exec->GetPropertyManager()->Tie( property_name.c_str(), &WOW );
property_name = base_property_name + "/wheel-speed-fps";
- Exec->GetPropertyManager()->Tie( property_name.c_str(), &RollingWhlVel );
+ Exec->GetPropertyManager()->Tie( property_name.c_str(), (FGLGear*)this,
+ &FGLGear::GetWheelRollVel);
property_name = base_property_name + "/z-position";
Exec->GetPropertyManager()->Tie( property_name.c_str(), (FGLGear*)this,
&FGLGear::GetZPosition, &FGLGear::SetZPosition);
double GetLocalGear(int idx) const { return vLocalGear(idx); }
/// Gets the name of the gear
- inline string GetName(void) const {return name; }
+ string GetName(void) const {return name; }
/// Gets the Weight On Wheels flag value
- inline bool GetWOW(void) const {return WOW; }
+ bool GetWOW(void) const {return WOW; }
/// Gets the current compressed length of the gear in feet
- inline double GetCompLen(void) const {return compressLength;}
+ double GetCompLen(void) const {return compressLength;}
/// Gets the current gear compression velocity in ft/sec
- inline double GetCompVel(void) const {return compressSpeed; }
+ double GetCompVel(void) const {return compressSpeed; }
/// Gets the gear compression force in pounds
- inline double GetCompForce(void) const {return vForce(eZ); }
- inline double GetBrakeFCoeff(void) const {return BrakeFCoeff;}
+ double GetCompForce(void) const {return StrutForce; }
+ double GetBrakeFCoeff(void) const {return BrakeFCoeff;}
/// Gets the current normalized tire pressure
- inline double GetTirePressure(void) const { return TirePressureNorm; }
+ double GetTirePressure(void) const { return TirePressureNorm; }
/// Sets the new normalized tire pressure
- inline void SetTirePressure(double p) { TirePressureNorm = p; }
+ void SetTirePressure(double p) { TirePressureNorm = p; }
/// Sets the brake value in percent (0 - 100)
- inline void SetBrake(double bp) {brakePct = bp;}
+ void SetBrake(double bp) {brakePct = bp;}
/// Sets the weight-on-wheels flag.
void SetWOW(bool wow) {WOW = wow;}
/** Set the console touchdown reporting feature
@param flag true turns on touchdown reporting, false turns it off */
- inline void SetReport(bool flag) { ReportEnable = flag; }
+ void SetReport(bool flag) { ReportEnable = flag; }
/** Get the console touchdown reporting feature
@return true if reporting is turned on */
- inline bool GetReport(void) const { return ReportEnable; }
+ bool GetReport(void) const { return ReportEnable; }
double GetSteerNorm(void) const { return radtodeg/maxSteerAngle*SteerAngle; }
double GetDefaultSteerAngle(double cmd) const { return cmd*maxSteerAngle; }
double GetstaticFCoeff(void) const { return staticFCoeff; }
- inline int GetBrakeGroup(void) const { return (int)eBrakeGrp; }
- inline int GetSteerType(void) const { return (int)eSteerType; }
+ int GetBrakeGroup(void) const { return (int)eBrakeGrp; }
+ int GetSteerType(void) const { return (int)eSteerType; }
- inline double GetZPosition(void) const { return vXYZ(3); }
- inline void SetZPosition(double z) { vXYZ(3) = z; }
+ double GetZPosition(void) const { return vXYZ(3); }
+ void SetZPosition(double z) { vXYZ(3) = z; }
bool GetSteerable(void) const { return eSteerType != stFixed; }
- inline bool GetRetractable(void) const { return isRetractable; }
- inline bool GetGearUnitUp(void) const { return GearUp; }
- inline bool GetGearUnitDown(void) const { return GearDown; }
- inline double GetWheelSideForce(void) const { return SideForce; }
- inline double GetWheelRollForce(void) const { return RollingForce; }
- inline double GetWheelSideVel(void) const { return SideWhlVel; }
- inline double GetWheelRollVel(void) const { return RollingWhlVel; }
- inline double GetBodyXForce(void) const { return vLocalForce(eX); }
- inline double GetBodyYForce(void) const { return vLocalForce(eY); }
- inline double GetWheelSlipAngle(void) const { return WheelSlip; }
+ bool GetRetractable(void) const { return isRetractable; }
+ bool GetGearUnitUp(void) const { return GearUp; }
+ bool GetGearUnitDown(void) const { return GearDown; }
+ double GetWheelSideForce(void) const { return vLocalForce(eY); }
+ double GetWheelRollForce(void) const { return vLocalForce(eX); }
+ double GetWheelSideVel(void) const { return vWhlVelVec(eY); }
+ double GetWheelRollVel(void) const { return vWhlVelVec(eX); }
+ double GetBodyXForce(void) const { return vForce(eX); }
+ double GetBodyYForce(void) const { return vForce(eY); }
+ double GetWheelSlipAngle(void) const { return WheelSlip; }
double GetWheelVel(int axis) const { return vWhlVelVec(axis);}
bool IsBogey(void) const { return (eContactType == ctBOGEY);}
double GetGearUnitPos(void);
private:
int GearNumber;
+ FGMatrix33 Tg2b, Tb2g;
FGColumnVector3 vXYZ;
FGColumnVector3 vMoment;
FGColumnVector3 vWhlBodyVec;
FGColumnVector3 vLocalGear;
FGColumnVector3 vForce;
- FGColumnVector3 last_vForce; // remove this
FGColumnVector3 vLocalForce;
- FGColumnVector3 vWhlVelVec; // Velocity of this wheel (Local)
- FGColumnVector3 normal, cvel;
+ FGColumnVector3 vWhlVelVec, vLocalWhlVel; // Velocity of this wheel
+ FGColumnVector3 normal, cvel, vGroundNormal;
FGLocation contact, gearLoc;
FGTable *ForceY_Table;
double dT;
double TakeoffDistanceTraveled;
double TakeoffDistanceTraveled50ft;
double LandingDistanceTraveled;
- double MaximumStrutForce;
+ double MaximumStrutForce, StrutForce;
double MaximumStrutTravel;
- double SideWhlVel, RollingWhlVel;
- double RollingForce, SideForce, FCoeff;
+ double FCoeff;
double WheelSlip;
double TirePressureNorm;
- double SinWheel, CosWheel;
double GearPos;
bool useFCSGearPos;
bool WOW;
void ComputeSlipAngle(void);
void ComputeSideForceCoefficient(void);
void ComputeVerticalStrutForce(void);
+ void ComputeGroundCoordSys(void);
void CrashDetect(void);
void InitializeReporting(void);
void ResetReporting(void);