+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// 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) && (vGroundWhlVel.Magnitude(eX,eY) > 1E-3)) {
+
+ FGColumnVector3 velocityDirection = vGroundWhlVel;
+
+ StaticFriction = false;
+
+ velocityDirection(eZ) = 0.;
+ velocityDirection.Normalize();
+
+ LMultiplier[ftDynamic].ForceJacobian = mT * velocityDirection;
+ LMultiplier[ftDynamic].MomentJacobian = vWhlContactVec * LMultiplier[ftDynamic].ForceJacobian;
+ LMultiplier[ftDynamic].Max = 0.;
+ LMultiplier[ftDynamic].Min = -fabs(dynamicFCoeff * vFn(eZ));
+
+ // The Lagrange multiplier value obtained from the previous iteration is kept
+ // This is supposed to accelerate the convergence of the projected Gauss-Seidel
+ // algorithm. The code just below is to make sure that the initial value
+ // is consistent with the current friction coefficient and normal reaction.
+ LMultiplier[ftDynamic].value = Constrain(LMultiplier[ftDynamic].Min, LMultiplier[ftDynamic].value, LMultiplier[ftDynamic].Max);
+
+ GroundReactions->RegisterLagrangeMultiplier(&LMultiplier[ftDynamic]);
+ }
+ 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 = mT * FGColumnVector3(1.,0.,0.);
+ LMultiplier[ftSide].ForceJacobian = mT * FGColumnVector3(0.,1.,0.);
+ LMultiplier[ftRoll].MomentJacobian = vWhlContactVec * LMultiplier[ftRoll].ForceJacobian;
+ LMultiplier[ftSide].MomentJacobian = vWhlContactVec * LMultiplier[ftSide].ForceJacobian;
+
+ switch(eContactType) {
+ case ctBOGEY:
+ LMultiplier[ftRoll].Max = fabs(BrakeFCoeff * vFn(eZ));
+ LMultiplier[ftSide].Max = fabs(FCoeff * vFn(eZ));
+ break;
+ case ctSTRUCTURE:
+ LMultiplier[ftRoll].Max = fabs(staticFCoeff * vFn(eZ));
+ LMultiplier[ftSide].Max = LMultiplier[ftRoll].Max;
+ break;
+ }
+
+ LMultiplier[ftRoll].Min = -LMultiplier[ftRoll].Max;
+ LMultiplier[ftSide].Min = -LMultiplier[ftSide].Max;
+
+ // The Lagrange multiplier value obtained from the previous iteration is kept
+ // This is supposed to accelerate the convergence of the projected Gauss-Seidel
+ // algorithm. The code just below is to make sure that the initial value
+ // is consistent with the current friction coefficient and normal reaction.
+ 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);
+
+ GroundReactions->RegisterLagrangeMultiplier(&LMultiplier[ftRoll]);
+ GroundReactions->RegisterLagrangeMultiplier(&LMultiplier[ftSide]);
+ }
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// This routine is called after the Lagrange multiplier has been computed in
+// the FGAccelerations class. The friction forces of the landing gear are then
+// updated accordingly.
+void FGLGear::UpdateForces(void)
+{
+ if (StaticFriction) {
+ vFn(eX) = LMultiplier[ftRoll].value;
+ vFn(eY) = LMultiplier[ftSide].value;
+ }
+ else {
+ FGColumnVector3 forceDir = mT.Transposed() * LMultiplier[ftDynamic].ForceJacobian;
+ vFn(eX) = LMultiplier[ftDynamic].value * forceDir(eX);
+ vFn(eY) = LMultiplier[ftDynamic].value * forceDir(eY);
+ }
+}
+