+double FGLGear::GetGearUnitPos(void)
+{
+ // hack to provide backward compatibility to gear/gear-pos-norm property
+ if( useFCSGearPos || FCS->GetGearPos() != 1.0 ) {
+ useFCSGearPos = true;
+ return FCS->GetGearPos();
+ }
+ return GearPos;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Compute the jacobian entries for the friction forces resolution later
+// in FGPropagate
+
+void FGLGear::ComputeJacobian(const FGColumnVector3& vWhlContactVec)
+{
+ // When the point of contact is moving, dynamic friction is used
+ // This type of friction is limited to ctSTRUCTURE elements because their
+ // friction coefficient is the same in every directions
+ if ((eContactType == ctSTRUCTURE) && (vLocalWhlVel.Magnitude(eY,eZ) > 1E-3)) {
+ FGColumnVector3 velocityDirection = vLocalWhlVel;
+
+ StaticFriction = false;
+
+ velocityDirection(eX) = 0.;
+ velocityDirection.Normalize();
+
+ LMultiplier[ftDynamic].ForceJacobian = Transform()*velocityDirection;
+ LMultiplier[ftDynamic].MomentJacobian = vWhlContactVec * LMultiplier[ftDynamic].ForceJacobian;
+ LMultiplier[ftDynamic].Max = 0.;
+ LMultiplier[ftDynamic].Min = -fabs(dynamicFCoeff * vFn(eX));
+ LMultiplier[ftDynamic].value = Constrain(LMultiplier[ftDynamic].Min, LMultiplier[ftDynamic].value, LMultiplier[ftDynamic].Max);
+ }
+ else {
+ // Static friction is used for ctSTRUCTURE when the contact point is not moving.
+ // It is always used for ctBOGEY elements because the friction coefficients
+ // of a tyre depend on the direction of the movement (roll & side directions).
+ // This cannot be handled properly by the so-called "dynamic friction".
+ StaticFriction = true;
+
+ LMultiplier[ftRoll].ForceJacobian = Transform()*FGColumnVector3(0.,1.,0.);
+ LMultiplier[ftSide].ForceJacobian = Transform()*FGColumnVector3(0.,0.,1.);
+ LMultiplier[ftRoll].MomentJacobian = vWhlContactVec * LMultiplier[ftRoll].ForceJacobian;
+ LMultiplier[ftSide].MomentJacobian = vWhlContactVec * LMultiplier[ftSide].ForceJacobian;
+
+ switch(eContactType) {
+ case ctBOGEY:
+ LMultiplier[ftRoll].Max = fabs(BrakeFCoeff * vFn(eX));
+ LMultiplier[ftSide].Max = fabs(FCoeff * vFn(eX));
+ break;
+ case ctSTRUCTURE:
+ LMultiplier[ftRoll].Max = fabs(staticFCoeff * vFn(eX));
+ LMultiplier[ftSide].Max = fabs(staticFCoeff * vFn(eX));
+ break;
+ }
+
+ LMultiplier[ftRoll].Min = -LMultiplier[ftRoll].Max;
+ LMultiplier[ftSide].Min = -LMultiplier[ftSide].Max;
+ LMultiplier[ftRoll].value = Constrain(LMultiplier[ftRoll].Min, LMultiplier[ftRoll].value, LMultiplier[ftRoll].Max);
+ LMultiplier[ftSide].value = Constrain(LMultiplier[ftSide].Min, LMultiplier[ftSide].value, LMultiplier[ftSide].Max);
+ }
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// This function is used by the MultiplierIterator class to enumerate the
+// Lagrange multipliers of a landing gear. This allows to encapsulate the storage
+// of the multipliers in FGLGear without exposing it. From an outside point of
+// view, each FGLGear instance has a number of Lagrange multipliers which can be
+// accessed through this routine without knowing the exact constraint which they
+// model.
+
+FGPropagate::LagrangeMultiplier* FGLGear::GetMultiplierEntry(int entry)
+{
+ switch(entry) {
+ case 0:
+ if (StaticFriction)
+ return &LMultiplier[ftRoll];
+ else
+ return &LMultiplier[ftDynamic];
+ case 1:
+ if (StaticFriction)
+ return &LMultiplier[ftSide];
+ default:
+ return NULL;
+ }
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// This routine is called after the Lagrange multiplier has been computed. The
+// friction forces of the landing gear are then updated accordingly.
+FGColumnVector3& FGLGear::UpdateForces(void)