]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/models/FGLGear.cpp
62fe8cc439dfb8adfb5a028baf5e91145fe3d75a
[flightgear.git] / src / FDM / JSBSim / models / FGLGear.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3  Module:       FGLGear.cpp
4  Author:       Jon S. Berndt
5                Norman H. Princen
6  Date started: 11/18/99
7  Purpose:      Encapsulates the landing gear elements
8  Called by:    FGAircraft
9
10  ------------- Copyright (C) 1999  Jon S. Berndt (jsb@hal-pc.org) -------------
11
12  This program is free software; you can redistribute it and/or modify it under
13  the terms of the GNU General Public License as published by the Free Software
14  Foundation; either version 2 of the License, or (at your option) any later
15  version.
16
17  This program is distributed in the hope that it will be useful, but WITHOUT
18  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
20  details.
21
22  You should have received a copy of the GNU General Public License along with
23  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
24  Place - Suite 330, Boston, MA  02111-1307, USA.
25
26  Further information about the GNU General Public License can also be found on
27  the world wide web at http://www.gnu.org.
28
29 FUNCTIONAL DESCRIPTION
30 --------------------------------------------------------------------------------
31
32 HISTORY
33 --------------------------------------------------------------------------------
34 11/18/99   JSB   Created
35 01/30/01   NHP   Extended gear model to properly simulate steering and braking
36
37 /%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38 INCLUDES
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
40
41 #include "FGLGear.h"
42
43 namespace JSBSim {
44
45 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
46 DEFINITIONS
47 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
48
49 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50 GLOBAL DATA
51 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
52
53 static const char *IdSrc = "$Id$";
54 static const char *IdHdr = ID_LGEAR;
55
56 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
57 CLASS IMPLEMENTATION
58 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
59
60 FGLGear::FGLGear(Element* el, FGFDMExec* fdmex, int number) : Exec(fdmex),
61                  GearNumber(number)
62 {
63   Element *force_table=0;
64   string force_type="";
65
66   kSpring = bDamp = bDampRebound = dynamicFCoeff = staticFCoeff = rollingFCoeff = maxSteerAngle = 0;
67   sSteerType = sBrakeGroup = sSteerType = "";
68   isRetractable = 0;
69
70   name = el->GetAttributeValue("name");
71   sContactType = el->GetAttributeValue("type");
72   if (el->FindElement("spring_coeff"))
73     kSpring = el->FindElementValueAsNumberConvertTo("spring_coeff", "LBS/FT");
74   if (el->FindElement("damping_coeff"))
75     bDamp   = el->FindElementValueAsNumberConvertTo("damping_coeff", "LBS/FT/SEC");
76
77   if (el->FindElement("damping_coeff_rebound"))
78     bDampRebound   = el->FindElementValueAsNumberConvertTo("damping_coeff_rebound", "LBS/FT/SEC");
79   else
80     bDampRebound   = bDamp;
81
82   if (el->FindElement("dynamic_friction"))
83     dynamicFCoeff = el->FindElementValueAsNumber("dynamic_friction");
84   if (el->FindElement("static_friction"))
85     staticFCoeff = el->FindElementValueAsNumber("static_friction");
86   if (el->FindElement("rolling_friction"))
87     rollingFCoeff = el->FindElementValueAsNumber("rolling_friction");
88   if (el->FindElement("max_steer"))
89     maxSteerAngle = el->FindElementValueAsNumberConvertTo("max_steer", "DEG");
90   if (el->FindElement("retractable"))
91     isRetractable = (int)el->FindElementValueAsNumber("retractable");
92
93   ForceY_Table = 0;
94   force_table = el->FindElement("table");
95   while (force_table) {
96     force_type = force_table->GetAttributeValue("type");
97     if (force_type == "CORNERING_COEFF") {
98       ForceY_Table = new FGTable(Exec->GetPropertyManager(), force_table);
99     } else {
100       cerr << "Undefined force table for " << name << " contact point" << endl;
101     }
102     force_table = el->FindNextElement("table");
103   }
104
105   sBrakeGroup = el->FindElementValue("brake_group");
106
107   if (maxSteerAngle == 360) sSteerType = "CASTERED";
108   else if (maxSteerAngle == 0.0) sSteerType = "FIXED";
109   else sSteerType = "STEERABLE";
110
111   Element* element = el->FindElement("location");
112   if (element) vXYZ = element->FindElementTripletConvertTo("IN");
113   else {cerr << "No location given for contact " << name << endl; exit(-1);}
114
115   if      (sBrakeGroup == "LEFT"  ) eBrakeGrp = bgLeft;
116   else if (sBrakeGroup == "RIGHT" ) eBrakeGrp = bgRight;
117   else if (sBrakeGroup == "CENTER") eBrakeGrp = bgCenter;
118   else if (sBrakeGroup == "NOSE"  ) eBrakeGrp = bgNose;
119   else if (sBrakeGroup == "TAIL"  ) eBrakeGrp = bgTail;
120   else if (sBrakeGroup == "NONE"  ) eBrakeGrp = bgNone;
121   else if (sBrakeGroup.empty()    ) {eBrakeGrp = bgNone;
122                                      sBrakeGroup = "NONE (defaulted)";}
123   else {
124     cerr << "Improper braking group specification in config file: "
125          << sBrakeGroup << " is undefined." << endl;
126   }
127
128   if      (sSteerType == "STEERABLE") eSteerType = stSteer;
129   else if (sSteerType == "FIXED"    ) eSteerType = stFixed;
130   else if (sSteerType == "CASTERED" ) eSteerType = stCaster;
131   else if (sSteerType.empty()       ) {eSteerType = stFixed;
132                                        sSteerType = "FIXED (defaulted)";}
133   else {
134     cerr << "Improper steering type specification in config file: "
135          << sSteerType << " is undefined." << endl;
136   }
137
138   GearUp = false;
139   GearDown = true;
140   Servicable = true;
141
142 // Add some AI here to determine if gear is located properly according to its
143 // brake group type ??
144
145   State       = Exec->GetState();
146   Aircraft    = Exec->GetAircraft();
147   Propagate   = Exec->GetPropagate();
148   Auxiliary   = Exec->GetAuxiliary();
149   FCS         = Exec->GetFCS();
150   MassBalance = Exec->GetMassBalance();
151
152   WOW = lastWOW = true; // should the value be initialized to true?
153   ReportEnable = true;
154   FirstContact = false;
155   StartedGroundRun = false;
156   TakeoffReported = LandingReported = false;
157   LandingDistanceTraveled = TakeoffDistanceTraveled = TakeoffDistanceTraveled50ft = 0.0;
158   MaximumStrutForce = MaximumStrutTravel = 0.0;
159   SideForce = RollingForce = 0.0;
160   SinkRate = GroundSpeed = 0.0;
161
162   vWhlBodyVec = MassBalance->StructuralToBody(vXYZ);
163
164   vLocalGear = Propagate->GetTb2l() * vWhlBodyVec;
165
166   compressLength  = 0.0;
167   compressSpeed   = 0.0;
168   brakePct        = 0.0;
169   maxCompLen      = 0.0;
170
171   WheelSlip = last_WheelSlip = 0.0;
172   TirePressureNorm = 1.0;
173
174   Debug(0);
175 }
176
177 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
178
179 FGLGear::FGLGear(const FGLGear& lgear)
180 {
181   GearNumber = lgear.GearNumber;
182   State    = lgear.State;
183   Aircraft = lgear.Aircraft;
184   Propagate = lgear.Propagate;
185   Auxiliary = lgear.Auxiliary;
186   Exec     = lgear.Exec;
187   FCS      = lgear.FCS;
188   MassBalance = lgear.MassBalance;
189
190   vXYZ = lgear.vXYZ;
191   vMoment = lgear.vMoment;
192   vWhlBodyVec = lgear.vWhlBodyVec;
193   vLocalGear = lgear.vLocalGear;
194
195   WOW                = lgear.WOW;
196   lastWOW            = lgear.lastWOW;
197   ReportEnable       = lgear.ReportEnable;
198   FirstContact       = lgear.FirstContact;
199   StartedGroundRun   = lgear.StartedGroundRun;
200   LandingDistanceTraveled   = lgear.LandingDistanceTraveled;
201   TakeoffDistanceTraveled   = lgear.TakeoffDistanceTraveled;
202   TakeoffDistanceTraveled50ft   = lgear.TakeoffDistanceTraveled50ft;
203   MaximumStrutForce  = lgear.MaximumStrutForce;
204   MaximumStrutTravel = lgear.MaximumStrutTravel;
205   SideForce          = lgear.SideForce;
206   RollingForce       = lgear.RollingForce;
207
208   kSpring         = lgear.kSpring;
209   bDamp           = lgear.bDamp;
210   bDampRebound    = lgear.bDampRebound;
211   compressLength  = lgear.compressLength;
212   compressSpeed   = lgear.compressSpeed;
213   staticFCoeff    = lgear.staticFCoeff;
214   dynamicFCoeff   = lgear.dynamicFCoeff;
215   rollingFCoeff   = lgear.rollingFCoeff;
216   brakePct        = lgear.brakePct;
217   maxCompLen      = lgear.maxCompLen;
218   SinkRate        = lgear.SinkRate;
219   GroundSpeed     = lgear.GroundSpeed;
220   LandingReported = lgear.LandingReported;
221   TakeoffReported = lgear.TakeoffReported;
222   name            = lgear.name;
223   sSteerType      = lgear.sSteerType;
224   sRetractable    = lgear.sRetractable;
225   sContactType    = lgear.sContactType;
226   sBrakeGroup     = lgear.sBrakeGroup;
227   eSteerType      = lgear.eSteerType;
228   eBrakeGrp       = lgear.eBrakeGrp;
229   maxSteerAngle   = lgear.maxSteerAngle;
230   isRetractable   = lgear.isRetractable;
231   GearUp          = lgear.GearUp;
232   GearDown        = lgear.GearDown;
233   WheelSlip       = lgear.WheelSlip;
234   TirePressureNorm = lgear.TirePressureNorm;
235   Servicable      = lgear.Servicable;
236   ForceY_Table    = lgear.ForceY_Table;
237   CosWheel        = lgear.CosWheel;
238   SinWheel        = lgear.SinWheel;
239   In              = lgear.In;
240   prevIn          = lgear.prevIn;
241   prevOut         = lgear.prevOut;
242   slipIn          = lgear.slipIn;
243   last_SlipIn     = lgear.last_SlipIn;
244   last_WheelSlip  = lgear.last_WheelSlip;
245 }
246
247 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
248
249 FGLGear::~FGLGear()
250 {
251   Debug(1);
252 }
253
254 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255
256 FGColumnVector3& FGLGear::Force(void)
257 {
258   FGColumnVector3 normal, cvel;
259   FGLocation contact, gearLoc;
260   double t = Exec->GetState()->Getsim_time();
261
262   vForce.InitMatrix();
263   vMoment.InitMatrix();
264
265   if (isRetractable) ComputeRetractionState();
266
267   if (GearUp) return vForce;
268
269   vWhlBodyVec = MassBalance->StructuralToBody(vXYZ); // Get wheel in body frame
270   vLocalGear = Propagate->GetTb2l() * vWhlBodyVec; // Get local frame wheel location
271
272   gearLoc = Propagate->GetLocation().LocalToLocation(vLocalGear);
273   compressLength = -Exec->GetGroundCallback()->GetAGLevel(t, gearLoc, contact, normal, cvel);
274
275   // The compression length is measured in the Z-axis, only, at this time.
276
277   if (compressLength > 0.00) {
278
279     WOW = true;
280
281     // [The next equation should really use the vector to the contact patch of
282     // the tire including the strut compression and not the original vWhlBodyVec.]
283
284     vWhlVelVec      =  Propagate->GetTb2l() * (Propagate->GetPQR() * vWhlBodyVec);
285     vWhlVelVec     +=  Propagate->GetVel() - cvel;
286     compressSpeed   =  vWhlVelVec(eZ);
287
288     InitializeReporting();
289     ComputeBrakeForceCoefficient();
290     ComputeSteeringAngle();
291     ComputeSlipAngle();
292     ComputeSideForceCoefficient();
293     ComputeVerticalStrutForce();
294
295     // Compute the forces in the wheel ground plane.
296
297     RollingForce = (1.0 - TirePressureNorm) * 30
298                    + vLocalForce(eZ) * BrakeFCoeff * (RollingWhlVel>=0?1.0:-1.0);
299     SideForce    = vLocalForce(eZ) * FCoeff;
300
301     // Transform these forces back to the local reference frame.
302
303     vLocalForce(eX) = RollingForce*CosWheel - SideForce*SinWheel;
304     vLocalForce(eY) = SideForce*CosWheel    + RollingForce*SinWheel;
305
306     // Transform the forces back to the body frame and compute the moment.
307
308     vForce  = Propagate->GetTl2b() * vLocalForce;
309
310     // Lag and attenuate the XY-plane forces dependent on velocity
311
312     double RFRV = 0.015; // Rolling force relaxation velocity
313     double SFRV = 0.25;  // Side force relaxation velocity
314     double dT = State->Getdt()*Exec->GetGroundReactions()->GetRate();
315
316     In = vForce;
317     vForce(eX) = (0.25)*(In(eX) + prevIn(eX)) + (0.50)*prevOut(eX);
318     vForce(eY) = (0.15)*(In(eY) + prevIn(eY)) + (0.70)*prevOut(eY);
319     prevOut = vForce;
320     prevIn = In;
321
322     if (fabs(RollingWhlVel) <= RFRV) vForce(eX) *= fabs(RollingWhlVel)/RFRV;
323     if (fabs(SideWhlVel) <= SFRV) vForce(eY) *= fabs(SideWhlVel)/SFRV;
324
325     vMoment = vWhlBodyVec * vForce;
326
327   } else { // Gear is NOT compressed
328
329     WOW = false;
330     compressLength = 0.0;
331
332     // Return to neutral position between 1.0 and 0.8 gear pos.
333     SteerAngle *= max(FCS->GetGearPos()-0.8, 0.0)/0.2;
334
335     ResetReporting();
336   }
337
338   ReportTakeoffOrLanding();
339   CrashDetect();
340
341   return vForce;
342 }
343
344 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
345
346 void FGLGear::ComputeRetractionState(void)
347 {
348   if (FCS->GetGearPos() < 0.01) {
349     GearUp   = true;
350     GearDown = false;
351   } else if (FCS->GetGearPos() > 0.99) {
352     GearDown = true;
353     GearUp   = false;
354   } else {
355     GearUp   = false;
356     GearDown = false;
357   }
358 }
359
360 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
361
362 void FGLGear::ComputeSlipAngle(void)
363 {
364   double dT = State->Getdt()*Exec->GetGroundReactions()->GetRate();
365
366   // Transform the wheel velocities from the local axis system to the wheel axis system.
367
368   RollingWhlVel = vWhlVelVec(eX)*CosWheel + vWhlVelVec(eY)*SinWheel;
369   SideWhlVel    = vWhlVelVec(eY)*CosWheel - vWhlVelVec(eX)*SinWheel;
370
371   // Calculate tire slip angle.
372
373   if (fabs(RollingWhlVel) < 0.1 && fabs(SideWhlVel) < 0.01) {
374     WheelSlip = -SteerAngle*radtodeg;
375   } else {
376     WheelSlip = atan2(SideWhlVel, fabs(RollingWhlVel))*radtodeg;
377   }
378   slipIn = WheelSlip;
379   WheelSlip = (0.46)*(slipIn + last_SlipIn) + (0.08)*last_WheelSlip;
380   last_WheelSlip = WheelSlip;
381   last_SlipIn = slipIn;
382 }
383
384 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
385 // Compute the steering angle in any case.
386 // This will also make sure that animations will look right.
387
388 void FGLGear::ComputeSteeringAngle(void)
389 {
390   switch (eSteerType) {
391   case stSteer:
392     SteerAngle = degtorad * FCS->GetSteerPosDeg(GearNumber);
393     break;
394   case stFixed:
395     SteerAngle = 0.0;
396     break;
397   case stCaster:
398     // This is not correct for castering gear. Should make steer angle parallel
399     // to the actual velocity vector of the wheel, given aircraft velocity vector
400     // and omega.
401     SteerAngle = 0.0;
402     break;
403   default:
404     cerr << "Improper steering type membership detected for this gear." << endl;
405     break;
406   }
407
408   SinWheel      = sin(Propagate->GetEuler(ePsi) + SteerAngle);
409   CosWheel      = cos(Propagate->GetEuler(ePsi) + SteerAngle);
410 }
411
412 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
413 // Reset reporting functionality after takeoff
414
415 void FGLGear::ResetReporting(void)
416 {
417   if (Propagate->GetDistanceAGL() > 200.0) {
418     FirstContact = false;
419     StartedGroundRun = false;
420     LandingReported = false;
421     LandingDistanceTraveled = 0.0;
422     MaximumStrutForce = MaximumStrutTravel = 0.0;
423   }
424 }
425
426 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
427
428 void FGLGear::InitializeReporting(void)
429 {
430   // If this is the first time the wheel has made contact, remember some values
431   // for later printout.
432
433   if (!FirstContact) {
434     FirstContact  = true;
435     SinkRate      =  compressSpeed;
436     GroundSpeed   =  Propagate->GetVel().Magnitude();
437     TakeoffReported = false;
438   }
439
440   // If the takeoff run is starting, initialize.
441
442   if ((Propagate->GetVel().Magnitude() > 0.1) &&
443       (FCS->GetBrake(bgLeft) == 0) &&
444       (FCS->GetBrake(bgRight) == 0) &&
445       (FCS->GetThrottlePos(0) == 1) && !StartedGroundRun)
446   {
447     TakeoffDistanceTraveled = 0;
448     TakeoffDistanceTraveled50ft = 0;
449     StartedGroundRun = true;
450   }
451 }
452
453 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
454 // Takeoff and landing reporting functionality
455
456 void FGLGear::ReportTakeoffOrLanding(void)
457 {
458   double deltaT = State->Getdt()*Exec->GetGroundReactions()->GetRate();
459
460   if (FirstContact) LandingDistanceTraveled += Auxiliary->GetVground()*deltaT;
461
462   if (StartedGroundRun) {
463      TakeoffDistanceTraveled50ft += Auxiliary->GetVground()*deltaT;
464     if (WOW) TakeoffDistanceTraveled += Auxiliary->GetVground()*deltaT;
465   }
466
467   if (ReportEnable && Auxiliary->GetVground() <= 0.05 && !LandingReported) {
468     if (debug_lvl > 0) Report(erLand);
469   }
470
471   if (ReportEnable && !TakeoffReported &&
472      (vLocalGear(eZ) - Propagate->GetDistanceAGL()) < -50.0)
473   {
474     if (debug_lvl > 0) Report(erTakeoff);
475   }
476
477   if (lastWOW != WOW) PutMessage("GEAR_CONTACT: " + name, WOW);
478   lastWOW = WOW;
479 }
480
481 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482 // Crash detection logic (really out-of-bounds detection)
483
484 void FGLGear::CrashDetect(void)
485 {
486   if (compressLength > 500.0 ||
487       vForce.Magnitude() > 100000000.0 ||
488       vMoment.Magnitude() > 5000000000.0 ||
489       SinkRate > 1.4666*30)
490   {
491     PutMessage("Crash Detected: Simulation FREEZE.");
492     State->SuspendIntegration();
493   }
494 }
495
496 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
497 // The following needs work regarding friction coefficients and braking and
498 // steering The BrakeFCoeff formula assumes that an anti-skid system is used.
499 // It also assumes that we won't be turning and braking at the same time.
500 // Will fix this later.
501 // [JSB] The braking force coefficients include normal rolling coefficient +
502 // a percentage of the static friction coefficient based on braking applied.
503
504 void FGLGear::ComputeBrakeForceCoefficient(void)
505 {
506   switch (eBrakeGrp) {
507   case bgLeft:
508     BrakeFCoeff =  ( rollingFCoeff*(1.0 - FCS->GetBrake(bgLeft)) +
509                      staticFCoeff*FCS->GetBrake(bgLeft) );
510     break;
511   case bgRight:
512     BrakeFCoeff =  ( rollingFCoeff*(1.0 - FCS->GetBrake(bgRight)) +
513                      staticFCoeff*FCS->GetBrake(bgRight) );
514     break;
515   case bgCenter:
516     BrakeFCoeff =  ( rollingFCoeff*(1.0 - FCS->GetBrake(bgCenter)) +
517                      staticFCoeff*FCS->GetBrake(bgCenter) );
518     break;
519   case bgNose:
520     BrakeFCoeff =  ( rollingFCoeff*(1.0 - FCS->GetBrake(bgCenter)) +
521                      staticFCoeff*FCS->GetBrake(bgCenter) );
522     break;
523   case bgTail:
524     BrakeFCoeff =  ( rollingFCoeff*(1.0 - FCS->GetBrake(bgCenter)) +
525                      staticFCoeff*FCS->GetBrake(bgCenter) );
526     break;
527   case bgNone:
528     BrakeFCoeff =  rollingFCoeff;
529     break;
530   default:
531     cerr << "Improper brake group membership detected for this gear." << endl;
532     break;
533   }
534 }
535
536 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
537 // Compute the sideforce coefficients using similar assumptions to LaRCSim for now.
538 // Allow a maximum of 10 degrees tire slip angle before wheel slides.  At that point,
539 // transition from static to dynamic friction.  There are more complicated formulations
540 // of this that avoid the discrete jump (similar to Pacejka).  Will fix this later.
541
542 void FGLGear::ComputeSideForceCoefficient(void)
543 {
544   if (ForceY_Table) {
545
546     FCoeff = ForceY_Table->GetValue(WheelSlip);
547
548   } else {
549
550     if (fabs(WheelSlip) <= 10.0) {
551       FCoeff = staticFCoeff*WheelSlip/10.0;
552     } else if (fabs(WheelSlip) <= 40.0) {
553       FCoeff = (dynamicFCoeff*(fabs(WheelSlip) - 10.0)/10.0
554                 + staticFCoeff*(40.0 - fabs(WheelSlip))/10.0)*(WheelSlip>=0?1.0:-1.0);
555     } else {
556       FCoeff = dynamicFCoeff*(WheelSlip>=0?1.0:-1.0);
557     }
558   }
559 }
560
561 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
562 // Compute the vertical force on the wheel using square-law damping (per comment
563 // in paper AIAA-2000-4303 - see header prologue comments). We might consider
564 // allowing for both square and linear damping force calculation. Also need to
565 // possibly give a "rebound damping factor" that differs from the compression
566 // case.
567
568 void FGLGear::ComputeVerticalStrutForce(void)
569 {
570   double springForce = 0;
571   double dampForce = 0;
572
573   springForce = -compressLength * kSpring;
574
575   if (compressSpeed >= 0.0) {
576     dampForce   = -compressSpeed * bDamp;
577   } else {
578     dampForce   = -compressSpeed * bDampRebound;
579   }
580   vLocalForce(eZ) =  min(springForce + dampForce, (double)0.0);
581
582   // Remember these values for reporting
583   MaximumStrutForce = max(MaximumStrutForce, fabs(vLocalForce(eZ)));
584   MaximumStrutTravel = max(MaximumStrutTravel, fabs(compressLength));
585 }
586
587 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
588
589 void FGLGear::bind(void)
590 {
591   char property_name[80];
592   snprintf(property_name, 80, "gear/unit[%d]/slip-angle-deg", GearNumber);
593   Exec->GetPropertyManager()->Tie( property_name, &WheelSlip );
594 }
595
596 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
597
598 void FGLGear::unbind(void)
599 {
600   char property_name[80];
601   snprintf(property_name, 80, "gear/unit[%d]/slip-angle-deg", GearNumber);
602   Exec->GetPropertyManager()->Untie( property_name );
603 }
604
605 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
606
607 void FGLGear::Report(ReportType repType)
608 {
609   switch(repType) {
610   case erLand:
611     cout << endl << "Touchdown report for " << name << endl;
612     cout << "  Sink rate at contact:  " << SinkRate                << " fps,    "
613                                 << SinkRate*0.3048          << " mps"     << endl;
614     cout << "  Contact ground speed:  " << GroundSpeed*.5925       << " knots,  "
615                                 << GroundSpeed*0.3048       << " mps"     << endl;
616     cout << "  Maximum contact force: " << MaximumStrutForce       << " lbs,    "
617                                 << MaximumStrutForce*4.448  << " Newtons" << endl;
618     cout << "  Maximum strut travel:  " << MaximumStrutTravel*12.0 << " inches, "
619                                 << MaximumStrutTravel*30.48 << " cm"      << endl;
620     cout << "  Distance traveled:     " << LandingDistanceTraveled        << " ft,     "
621                                 << LandingDistanceTraveled*0.3048  << " meters"  << endl;
622     LandingReported = true;
623     break;
624   case erTakeoff:
625     cout << endl << "Takeoff report for " << name << endl;
626     cout << "  Distance traveled:                " << TakeoffDistanceTraveled
627          << " ft,     " << TakeoffDistanceTraveled*0.3048  << " meters"  << endl;
628     cout << "  Distance traveled (over 50'):     " << TakeoffDistanceTraveled50ft
629          << " ft,     " << TakeoffDistanceTraveled50ft*0.3048 << " meters" << endl;
630     TakeoffReported = true;
631     break;
632   }
633 }
634
635 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
636 //    The bitmasked value choices are as follows:
637 //    unset: In this case (the default) JSBSim would only print
638 //       out the normally expected messages, essentially echoing
639 //       the config files as they are read. If the environment
640 //       variable is not set, debug_lvl is set to 1 internally
641 //    0: This requests JSBSim not to output any messages
642 //       whatsoever.
643 //    1: This value explicity requests the normal JSBSim
644 //       startup messages
645 //    2: This value asks for a message to be printed out when
646 //       a class is instantiated
647 //    4: When this value is set, a message is displayed when a
648 //       FGModel object executes its Run() method
649 //    8: When this value is set, various runtime state variables
650 //       are printed out periodically
651 //    16: When set various parameters are sanity checked and
652 //       a message is printed out when they go out of bounds
653
654 void FGLGear::Debug(int from)
655 {
656   if (debug_lvl <= 0) return;
657
658   if (debug_lvl & 1) { // Standard console startup message output
659     if (from == 0) { // Constructor - loading and initialization
660       cout << "    " << sContactType << " " << name          << endl;
661       cout << "      Location: "         << vXYZ          << endl;
662       cout << "      Spring Constant:  " << kSpring       << endl;
663       cout << "      Damping Constant: " << bDamp         << endl;
664       cout << "      Dynamic Friction: " << dynamicFCoeff << endl;
665       cout << "      Static Friction:  " << staticFCoeff  << endl;
666       if (sContactType == "BOGEY") {
667         cout << "      Rolling Friction: " << rollingFCoeff << endl;
668         cout << "      Steering Type:    " << sSteerType    << endl;
669         cout << "      Grouping:         " << sBrakeGroup   << endl;
670         cout << "      Max Steer Angle:  " << maxSteerAngle << endl;
671         cout << "      Retractable:      " << isRetractable  << endl;
672       }
673     }
674   }
675   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
676     if (from == 0) cout << "Instantiated: FGLGear" << endl;
677     if (from == 1) cout << "Destroyed:    FGLGear" << endl;
678   }
679   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
680   }
681   if (debug_lvl & 8 ) { // Runtime state variables
682   }
683   if (debug_lvl & 16) { // Sanity checking
684   }
685   if (debug_lvl & 64) {
686     if (from == 0) { // Constructor
687       cout << IdSrc << endl;
688       cout << IdHdr << endl;
689     }
690   }
691 }
692
693 } // namespace JSBSim
694