]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/models/FGLGear.cpp
a470fd5f4727655f68e31adaf27d8bf76deaef32
[flightgear.git] / src / FDM / JSBSim / models / FGLGear.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3  Module:       FGLGear.cpp
4  Author:       Jon S. Berndt
5                Norman H. Princen
6                Bertrand Coconnier
7  Date started: 11/18/99
8  Purpose:      Encapsulates the landing gear elements
9  Called by:    FGAircraft
10
11  ------------- Copyright (C) 1999  Jon S. Berndt (jon@jsbsim.org) -------------
12
13  This program is free software; you can redistribute it and/or modify it under
14  the terms of the GNU Lesser General Public License as published by the Free Software
15  Foundation; either version 2 of the License, or (at your option) any later
16  version.
17
18  This program is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20  FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
21  details.
22
23  You should have received a copy of the GNU Lesser General Public License along with
24  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
25  Place - Suite 330, Boston, MA  02111-1307, USA.
26
27  Further information about the GNU Lesser General Public License can also be found on
28  the world wide web at http://www.gnu.org.
29
30 FUNCTIONAL DESCRIPTION
31 --------------------------------------------------------------------------------
32
33 HISTORY
34 --------------------------------------------------------------------------------
35 11/18/99   JSB   Created
36 01/30/01   NHP   Extended gear model to properly simulate steering and braking
37 07/08/09   BC    Modified gear model to support large angles between aircraft and ground
38
39 /%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40 INCLUDES
41 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
42
43 #include "FGLGear.h"
44
45 namespace JSBSim {
46
47 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
48 DEFINITIONS
49 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
50
51 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
52 GLOBAL DATA
53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
54
55 static const char *IdSrc = "$Id$";
56 static const char *IdHdr = ID_LGEAR;
57
58 // Body To Structural (body frame is rotated 180 deg about Y and lengths are given in
59 // ft instead of inches)
60 const FGMatrix33 FGLGear::Tb2s(-1./inchtoft, 0., 0., 0., 1./inchtoft, 0., 0., 0., -1./inchtoft);
61
62 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
63 CLASS IMPLEMENTATION
64 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
65
66 FGLGear::FGLGear(Element* el, FGFDMExec* fdmex, int number) :
67   FGForce(fdmex),
68   GearNumber(number),
69   SteerAngle(0.0)
70 {
71   Element *force_table=0;
72   Element *dampCoeff=0;
73   Element *dampCoeffRebound=0;
74   string force_type="";
75
76   kSpring = bDamp = bDampRebound = dynamicFCoeff = staticFCoeff = rollingFCoeff = maxSteerAngle = 0;
77   sSteerType = sBrakeGroup = sSteerType = "";
78   isRetractable = 0;
79   eDampType = dtLinear;
80   eDampTypeRebound = dtLinear;
81
82   name = el->GetAttributeValue("name");
83   sContactType = el->GetAttributeValue("type");
84   if (sContactType == "BOGEY") {
85     eContactType = ctBOGEY;
86   } else if (sContactType == "STRUCTURE") {
87     eContactType = ctSTRUCTURE;
88   } else {
89     // Unknown contact point types will be treated as STRUCTURE.
90     eContactType = ctSTRUCTURE;
91   }
92
93   if (el->FindElement("spring_coeff"))
94     kSpring = el->FindElementValueAsNumberConvertTo("spring_coeff", "LBS/FT");
95   if (el->FindElement("damping_coeff")) {
96     dampCoeff = el->FindElement("damping_coeff");
97     if (dampCoeff->GetAttributeValue("type") == "SQUARE") {
98       eDampType = dtSquare;
99       bDamp   = el->FindElementValueAsNumberConvertTo("damping_coeff", "LBS/FT2/SEC2");
100     } else {
101       bDamp   = el->FindElementValueAsNumberConvertTo("damping_coeff", "LBS/FT/SEC");
102     }
103   }
104
105   if (el->FindElement("damping_coeff_rebound")) {
106     dampCoeffRebound = el->FindElement("damping_coeff_rebound");
107     if (dampCoeffRebound->GetAttributeValue("type") == "SQUARE") {
108       eDampTypeRebound = dtSquare;
109       bDampRebound   = el->FindElementValueAsNumberConvertTo("damping_coeff_rebound", "LBS/FT2/SEC2");
110     } else {
111       bDampRebound   = el->FindElementValueAsNumberConvertTo("damping_coeff_rebound", "LBS/FT/SEC");
112     }
113   } else {
114     bDampRebound   = bDamp;
115     eDampTypeRebound = eDampType;
116   }
117
118   if (el->FindElement("dynamic_friction"))
119     dynamicFCoeff = el->FindElementValueAsNumber("dynamic_friction");
120   if (el->FindElement("static_friction"))
121     staticFCoeff = el->FindElementValueAsNumber("static_friction");
122   if (el->FindElement("rolling_friction"))
123     rollingFCoeff = el->FindElementValueAsNumber("rolling_friction");
124   if (el->FindElement("max_steer"))
125     maxSteerAngle = el->FindElementValueAsNumberConvertTo("max_steer", "DEG");
126   if (el->FindElement("retractable"))
127     isRetractable = ((unsigned int)el->FindElementValueAsNumber("retractable"))>0.0?true:false;
128
129   ForceY_Table = 0;
130   force_table = el->FindElement("table");
131   while (force_table) {
132     force_type = force_table->GetAttributeValue("type");
133     if (force_type == "CORNERING_COEFF") {
134       ForceY_Table = new FGTable(fdmex->GetPropertyManager(), force_table);
135     } else {
136       cerr << "Undefined force table for " << name << " contact point" << endl;
137     }
138     force_table = el->FindNextElement("table");
139   }
140
141   sBrakeGroup = el->FindElementValue("brake_group");
142
143   if (maxSteerAngle == 360) sSteerType = "CASTERED";
144   else if (maxSteerAngle == 0.0) sSteerType = "FIXED";
145   else sSteerType = "STEERABLE";
146
147   Element* element = el->FindElement("location");
148   if (element) vXYZn = element->FindElementTripletConvertTo("IN");
149   else {cerr << "No location given for contact " << name << endl; exit(-1);}
150   SetTransformType(FGForce::tCustom);
151
152   element = el->FindElement("orientation");
153   if (element && (eContactType == ctBOGEY)) {
154     vGearOrient = element->FindElementTripletConvertTo("RAD");
155
156     double cp,sp,cr,sr,cy,sy;
157
158     cp=cos(vGearOrient(ePitch)); sp=sin(vGearOrient(ePitch));
159     cr=cos(vGearOrient(eRoll));  sr=sin(vGearOrient(eRoll));
160     cy=cos(vGearOrient(eYaw));   sy=sin(vGearOrient(eYaw));
161
162     mTGear(1,1) =  cp*cy;
163     mTGear(2,1) =  cp*sy;
164     mTGear(3,1) = -sp;
165
166     mTGear(1,2) = sr*sp*cy - cr*sy;
167     mTGear(2,2) = sr*sp*sy + cr*cy;
168     mTGear(3,2) = sr*cp;
169
170     mTGear(1,3) = cr*sp*cy + sr*sy;
171     mTGear(2,3) = cr*sp*sy - sr*cy;
172     mTGear(3,3) = cr*cp;
173   }
174   else {
175     mTGear(1,1) = 1.;
176     mTGear(2,2) = 1.;
177     mTGear(3,3) = 1.;
178   }
179
180   if      (sBrakeGroup == "LEFT"  ) eBrakeGrp = bgLeft;
181   else if (sBrakeGroup == "RIGHT" ) eBrakeGrp = bgRight;
182   else if (sBrakeGroup == "CENTER") eBrakeGrp = bgCenter;
183   else if (sBrakeGroup == "NOSE"  ) eBrakeGrp = bgNose;
184   else if (sBrakeGroup == "TAIL"  ) eBrakeGrp = bgTail;
185   else if (sBrakeGroup == "NONE"  ) eBrakeGrp = bgNone;
186   else if (sBrakeGroup.empty()    ) {eBrakeGrp = bgNone;
187                                      sBrakeGroup = "NONE (defaulted)";}
188   else {
189     cerr << "Improper braking group specification in config file: "
190          << sBrakeGroup << " is undefined." << endl;
191   }
192
193   if      (sSteerType == "STEERABLE") eSteerType = stSteer;
194   else if (sSteerType == "FIXED"    ) eSteerType = stFixed;
195   else if (sSteerType == "CASTERED" ) eSteerType = stCaster;
196   else if (sSteerType.empty()       ) {eSteerType = stFixed;
197                                        sSteerType = "FIXED (defaulted)";}
198   else {
199     cerr << "Improper steering type specification in config file: "
200          << sSteerType << " is undefined." << endl;
201   }
202
203   RFRV = 0.7;  // Rolling force relaxation velocity, default value
204   SFRV = 0.7;  // Side force relaxation velocity, default value
205
206   Element* relax_vel = el->FindElement("relaxation_velocity");
207   if (relax_vel) {
208     if (relax_vel->FindElement("rolling")) {
209       RFRV = relax_vel->FindElementValueAsNumberConvertTo("rolling", "FT/SEC");
210     }
211     if (relax_vel->FindElement("side")) {
212       SFRV = relax_vel->FindElementValueAsNumberConvertTo("side", "FT/SEC");
213     }
214   }
215
216   State       = fdmex->GetState();
217   Aircraft    = fdmex->GetAircraft();
218   Propagate   = fdmex->GetPropagate();
219   Auxiliary   = fdmex->GetAuxiliary();
220   FCS         = fdmex->GetFCS();
221   MassBalance = fdmex->GetMassBalance();
222
223   LongForceLagFilterCoeff = 1/State->Getdt(); // default longitudinal force filter coefficient
224   LatForceLagFilterCoeff  = 1/State->Getdt(); // default lateral force filter coefficient
225
226   Element* force_lag_filter_elem = el->FindElement("force_lag_filter");
227   if (force_lag_filter_elem) {
228     if (force_lag_filter_elem->FindElement("rolling")) {
229       LongForceLagFilterCoeff = force_lag_filter_elem->FindElementValueAsNumber("rolling");
230     }
231     if (force_lag_filter_elem->FindElement("side")) {
232       LatForceLagFilterCoeff = force_lag_filter_elem->FindElementValueAsNumber("side");
233     }
234   }
235
236   LongForceFilter = Filter(LongForceLagFilterCoeff, State->Getdt());
237   LatForceFilter = Filter(LatForceLagFilterCoeff, State->Getdt());
238
239   WheelSlipLagFilterCoeff = 1/State->Getdt();
240
241   Element *wheel_slip_angle_lag_elem = el->FindElement("wheel_slip_filter");
242   if (wheel_slip_angle_lag_elem) {
243     WheelSlipLagFilterCoeff = wheel_slip_angle_lag_elem->GetDataAsNumber();
244   }
245   
246   WheelSlipFilter = Filter(WheelSlipLagFilterCoeff, State->Getdt());
247
248   GearUp = false;
249   GearDown = true;
250   GearPos  = 1.0;
251   useFCSGearPos = false;
252   Servicable = true;
253
254 // Add some AI here to determine if gear is located properly according to its
255 // brake group type ??
256
257   WOW = lastWOW = false;
258   ReportEnable = true;
259   FirstContact = false;
260   StartedGroundRun = false;
261   TakeoffReported = LandingReported = false;
262   LandingDistanceTraveled = TakeoffDistanceTraveled = TakeoffDistanceTraveled50ft = 0.0;
263   MaximumStrutForce = MaximumStrutTravel = 0.0;
264   SinkRate = GroundSpeed = 0.0;
265
266   vWhlBodyVec = MassBalance->StructuralToBody(vXYZn);
267   vLocalGear = Propagate->GetTb2l() * vWhlBodyVec;
268   vWhlVelVec.InitMatrix();
269
270   compressLength  = 0.0;
271   compressSpeed   = 0.0;
272   brakePct        = 0.0;
273   maxCompLen      = 0.0;
274
275   WheelSlip = 0.0;
276   TirePressureNorm = 1.0;
277
278   // Set Pacejka terms
279
280   Stiffness = 0.06;
281   Shape = 2.8;
282   Peak = staticFCoeff;
283   Curvature = 1.03;
284
285   Debug(0);
286 }
287
288 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
289
290 FGLGear::~FGLGear()
291 {
292   delete ForceY_Table;
293   Debug(1);
294 }
295
296 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
297
298 FGColumnVector3& FGLGear::GetBodyForces(void)
299 {
300   double t = fdmex->GetState()->Getsim_time();
301   dT = State->Getdt()*fdmex->GetGroundReactions()->GetRate();
302
303   vFn.InitMatrix();
304
305   if (isRetractable) ComputeRetractionState();
306
307   if (GearDown) {
308     double verticalZProj = 0.;
309
310     vWhlBodyVec = MassBalance->StructuralToBody(vXYZn); // Get wheel in body frame
311     vLocalGear = Propagate->GetTb2l() * vWhlBodyVec; // Get local frame wheel location
312
313     gearLoc = Propagate->GetLocation().LocalToLocation(vLocalGear);
314     // Compute the height of the theoretical location of the wheel (if strut is not compressed) with
315     // respect to the ground level
316     double height = fdmex->GetGroundCallback()->GetAGLevel(t, gearLoc, contact, normal, cvel);
317     vGroundNormal = -1. * Propagate->GetTec2b() * normal;
318
319     // The height returned above is the AGL and is expressed in the Z direction of the local
320     // coordinate frame. We now need to transform this height in actual compression of the strut (BOGEY)
321     // of in the normal direction to the ground (STRUCTURE)
322     switch (eContactType) {
323     case ctBOGEY:
324       verticalZProj = (Propagate->GetTb2l()*mTGear*FGColumnVector3(0.,0.,1.))(eZ);
325       compressLength = verticalZProj > 0.0 ? -height / verticalZProj : 0.0;
326       break;
327     case ctSTRUCTURE:
328       verticalZProj = (Propagate->GetTec2l()*normal)(eZ);
329       compressLength = fabs(verticalZProj) > 0.0 ? -height / verticalZProj : 0.0;
330       break;
331     }
332
333     if (compressLength > 0.00) {
334
335       WOW = true;
336
337       // [The next equation should really use the vector to the contact patch of
338       // the tire including the strut compression and not the original vWhlBodyVec.]
339
340       FGColumnVector3 vWhlDisplVec = mTGear * FGColumnVector3(0., 0., compressLength);
341       FGColumnVector3 vWhlContactVec = vWhlBodyVec - vWhlDisplVec;
342       vActingXYZn = vXYZn - Tb2s * vWhlDisplVec;
343       FGColumnVector3 vBodyWhlVel  = Propagate->GetPQR() * vWhlContactVec;
344       vBodyWhlVel += Propagate->GetUVW() - Propagate->GetTec2b() * cvel;
345
346       vWhlVelVec = mTGear.Transposed() * vBodyWhlVel;
347
348       InitializeReporting();
349       ComputeSteeringAngle();
350       ComputeGroundCoordSys();
351
352       vLocalWhlVel = Transform().Transposed() * vBodyWhlVel;
353
354       switch (eContactType) {
355       case ctBOGEY:
356         // Compression speed along the strut
357         compressSpeed = -vWhlVelVec(eZ);
358       case ctSTRUCTURE:
359         // Compression speed along the ground normal
360         compressSpeed = -vLocalWhlVel(eX);
361       }
362
363       ComputeVerticalStrutForce();
364
365       // Compute the forces in the wheel ground plane.
366       if (eContactType == ctBOGEY) {
367         ComputeSlipAngle();
368         ComputeBrakeForceCoefficient();
369         ComputeSideForceCoefficient();
370         double sign = vLocalWhlVel(eY)>0?1.0:(vLocalWhlVel(eY)<0?-1.0:0.0);
371         vFn(eY) = - ((1.0 - TirePressureNorm) * 30 + vFn(eX) * BrakeFCoeff) * sign;
372         vFn(eZ) = vFn(eX) * FCoeff;
373       }
374       else if (eContactType == ctSTRUCTURE) {
375         FGColumnVector3 vSlipVec = vLocalWhlVel;
376         vSlipVec(eX) = 0.;
377         vSlipVec.Normalize();
378         vFn -= staticFCoeff * vFn(eX) * vSlipVec;
379       }
380
381       // Lag and attenuate the XY-plane forces dependent on velocity. This code
382       // uses a lag filter, C/(s + C) where "C" is the filter coefficient. When
383       // "C" is chosen at the frame rate (in Hz), the jittering is significantly
384       // reduced. This is because the jitter is present *at* the execution rate.
385       // If a coefficient is set to something equal to or less than zero, the
386       // filter is bypassed.
387
388       if (LongForceLagFilterCoeff > 0) vFn(eY) = LongForceFilter.execute(vFn(eY));
389       if (LatForceLagFilterCoeff > 0)  vFn(eZ) = LatForceFilter.execute(vFn(eZ));
390
391       if ((fabs(vLocalWhlVel(eY)) <= RFRV) && RFRV > 0) vFn(eY) *= fabs(vLocalWhlVel(eY))/RFRV;
392       if ((fabs(vLocalWhlVel(eZ)) <= SFRV) && SFRV > 0) vFn(eZ) *= fabs(vLocalWhlVel(eZ))/SFRV;
393
394       // End section for attenuating gear jitter
395
396     } else { // Gear is NOT compressed
397
398       WOW = false;
399       compressLength = 0.0;
400       compressSpeed = 0.0;
401       WheelSlip = 0.0;
402       StrutForce = 0.0;
403
404       // Let wheel spin down slowly
405       vWhlVelVec(eX) -= 13.0*dT;
406       if (vWhlVelVec(eX) < 0.0) vWhlVelVec(eX) = 0.0;
407
408       // Return to neutral position between 1.0 and 0.8 gear pos.
409       SteerAngle *= max(GetGearUnitPos()-0.8, 0.0)/0.2;
410
411       ResetReporting();
412     }
413   }
414
415   ReportTakeoffOrLanding();
416
417   // Require both WOW and LastWOW to be true before checking crash conditions
418   // to allow the WOW flag to be used in terminating a scripted run.
419   if (WOW && lastWOW) CrashDetect();
420
421   lastWOW = WOW;
422
423   return FGForce::GetBodyForces();
424 }
425
426 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
427 // Build a local "ground" coordinate system defined by
428 //  eX : normal to the ground
429 //  eY : projection of the rolling direction on the ground
430 //  eZ : projection of the sliping direction on the ground
431
432 void FGLGear::ComputeGroundCoordSys(void)
433 {
434   // Euler angles are built up to create a local frame to describe the forces
435   // applied to the gear by the ground. Here pitch, yaw and roll do not have
436   // any physical meaning. It is just a convenient notation.
437   // First, "pitch" and "yaw" are determined in order to align eX with the
438   // ground normal.
439   if (vGroundNormal(eZ) < -1.0)
440     vOrient(ePitch) = 0.5*M_PI;
441   else if (1.0 < vGroundNormal(eZ))
442     vOrient(ePitch) = -0.5*M_PI;
443   else
444     vOrient(ePitch) = asin(-vGroundNormal(eZ));
445
446   if (fabs(vOrient(ePitch)) == 0.5*M_PI)
447     vOrient(eYaw) = 0.;
448   else
449     vOrient(eYaw) = atan2(vGroundNormal(eY), vGroundNormal(eX));
450   
451   vOrient(eRoll) = 0.;
452   UpdateCustomTransformMatrix();
453
454   if (eContactType == ctBOGEY) {
455     // In the case of a bogey, the third angle "roll" is used to align the axis eY and eZ
456     // to the rolling and sliping direction respectively.
457     FGColumnVector3 updatedRollingAxis = Transform().Transposed() * mTGear
458                                        * FGColumnVector3(-sin(SteerAngle), cos(SteerAngle), 0.);
459
460     vOrient(eRoll) = atan2(updatedRollingAxis(eY), -updatedRollingAxis(eZ));
461     UpdateCustomTransformMatrix();
462   }
463 }
464
465 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
466
467 void FGLGear::ComputeRetractionState(void)
468 {
469   double gearPos = GetGearUnitPos();
470   if (gearPos < 0.01) {
471     GearUp   = true;
472     WOW      = false;
473     GearDown = false;
474     vWhlVelVec.InitMatrix();
475   } else if (gearPos > 0.99) {
476     GearDown = true;
477     GearUp   = false;
478   } else {
479     GearUp   = false;
480     GearDown = false;
481   }
482 }
483
484 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485
486 void FGLGear::ComputeSlipAngle(void)
487 {
488   // Calculate tire slip angle.
489   WheelSlip = -atan2(vLocalWhlVel(eZ), fabs(vLocalWhlVel(eY)))*radtodeg;
490
491   // Filter the wheel slip angle
492   if (WheelSlipLagFilterCoeff > 0) WheelSlip = WheelSlipFilter.execute(WheelSlip);
493 }
494
495 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
496 // Compute the steering angle in any case.
497 // This will also make sure that animations will look right.
498
499 void FGLGear::ComputeSteeringAngle(void)
500 {
501   switch (eSteerType) {
502   case stSteer:
503     SteerAngle = degtorad * FCS->GetSteerPosDeg(GearNumber);
504     break;
505   case stFixed:
506     SteerAngle = 0.0;
507     break;
508   case stCaster:
509     SteerAngle = atan2(vWhlVelVec(eY), fabs(vWhlVelVec(eX)));
510     break;
511   default:
512     cerr << "Improper steering type membership detected for this gear." << endl;
513     break;
514   }
515 }
516
517 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
518 // Reset reporting functionality after takeoff
519
520 void FGLGear::ResetReporting(void)
521 {
522   if (Propagate->GetDistanceAGL() > 200.0) {
523     FirstContact = false;
524     StartedGroundRun = false;
525     LandingReported = false;
526     TakeoffReported = true;
527     LandingDistanceTraveled = 0.0;
528     MaximumStrutForce = MaximumStrutTravel = 0.0;
529   }
530 }
531
532 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
533
534 void FGLGear::InitializeReporting(void)
535 {
536   // If this is the first time the wheel has made contact, remember some values
537   // for later printout.
538
539   if (!FirstContact) {
540     FirstContact  = true;
541     SinkRate      =  compressSpeed;
542     GroundSpeed   =  Propagate->GetVel().Magnitude();
543     TakeoffReported = false;
544   }
545
546   // If the takeoff run is starting, initialize.
547
548   if ((Propagate->GetVel().Magnitude() > 0.1) &&
549       (FCS->GetBrake(bgLeft) == 0) &&
550       (FCS->GetBrake(bgRight) == 0) &&
551       (FCS->GetThrottlePos(0) > 0.90) && !StartedGroundRun)
552   {
553     TakeoffDistanceTraveled = 0;
554     TakeoffDistanceTraveled50ft = 0;
555     StartedGroundRun = true;
556   }
557 }
558
559 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
560 // Takeoff and landing reporting functionality
561
562 void FGLGear::ReportTakeoffOrLanding(void)
563 {
564   double deltaT = State->Getdt()*fdmex->GetGroundReactions()->GetRate();
565
566   if (FirstContact)
567     LandingDistanceTraveled += Auxiliary->GetVground()*deltaT;
568
569   if (StartedGroundRun) {
570     TakeoffDistanceTraveled50ft += Auxiliary->GetVground()*deltaT;
571     if (WOW) TakeoffDistanceTraveled += Auxiliary->GetVground()*deltaT;
572   }
573
574   if ( ReportEnable
575        && Auxiliary->GetVground() <= 0.05
576        && !LandingReported
577        && fdmex->GetGroundReactions()->GetWOW())
578   {
579     if (debug_lvl > 0) Report(erLand);
580   }
581
582   if ( ReportEnable
583        && !TakeoffReported
584        && (Propagate->GetDistanceAGL() - vLocalGear(eZ)) > 50.0
585        && !fdmex->GetGroundReactions()->GetWOW())
586   {
587     if (debug_lvl > 0) Report(erTakeoff);
588   }
589
590   if (lastWOW != WOW) PutMessage("GEAR_CONTACT: " + name, WOW);
591 }
592
593 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
594 // Crash detection logic (really out-of-bounds detection)
595
596 void FGLGear::CrashDetect(void)
597 {
598   if ( (compressLength > 500.0 ||
599       vFn.Magnitude() > 100000000.0 ||
600       GetMoments().Magnitude() > 5000000000.0 ||
601       SinkRate > 1.4666*30 ) && !State->IntegrationSuspended())
602   {
603     PutMessage("Crash Detected: Simulation FREEZE.");
604     State->SuspendIntegration();
605   }
606 }
607
608 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609 // The following needs work regarding friction coefficients and braking and
610 // steering The BrakeFCoeff formula assumes that an anti-skid system is used.
611 // It also assumes that we won't be turning and braking at the same time.
612 // Will fix this later.
613 // [JSB] The braking force coefficients include normal rolling coefficient +
614 // a percentage of the static friction coefficient based on braking applied.
615
616 void FGLGear::ComputeBrakeForceCoefficient(void)
617 {
618   switch (eBrakeGrp) {
619   case bgLeft:
620     BrakeFCoeff =  ( rollingFCoeff*(1.0 - FCS->GetBrake(bgLeft)) +
621                      staticFCoeff*FCS->GetBrake(bgLeft) );
622     break;
623   case bgRight:
624     BrakeFCoeff =  ( rollingFCoeff*(1.0 - FCS->GetBrake(bgRight)) +
625                      staticFCoeff*FCS->GetBrake(bgRight) );
626     break;
627   case bgCenter:
628     BrakeFCoeff =  ( rollingFCoeff*(1.0 - FCS->GetBrake(bgCenter)) +
629                      staticFCoeff*FCS->GetBrake(bgCenter) );
630     break;
631   case bgNose:
632     BrakeFCoeff =  ( rollingFCoeff*(1.0 - FCS->GetBrake(bgCenter)) +
633                      staticFCoeff*FCS->GetBrake(bgCenter) );
634     break;
635   case bgTail:
636     BrakeFCoeff =  ( rollingFCoeff*(1.0 - FCS->GetBrake(bgCenter)) +
637                      staticFCoeff*FCS->GetBrake(bgCenter) );
638     break;
639   case bgNone:
640     BrakeFCoeff =  rollingFCoeff;
641     break;
642   default:
643     cerr << "Improper brake group membership detected for this gear." << endl;
644     break;
645   }
646 }
647
648 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
649 // Compute the sideforce coefficients using Pacejka's Magic Formula.
650 //
651 //   y(x) = D sin {C arctan [Bx - E(Bx - arctan Bx)]}
652 //
653 // Where: B = Stiffness Factor (0.06, here)
654 //        C = Shape Factor (2.8, here)
655 //        D = Peak Factor (0.8, here)
656 //        E = Curvature Factor (1.03, here)
657
658 void FGLGear::ComputeSideForceCoefficient(void)
659 {
660   if (ForceY_Table) {
661     FCoeff = ForceY_Table->GetValue(WheelSlip);
662   } else {
663     double StiffSlip = Stiffness*WheelSlip;
664     FCoeff = Peak * sin(Shape*atan(StiffSlip - Curvature*(StiffSlip - atan(StiffSlip))));
665   }
666 }
667
668 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
669 // Compute the vertical force on the wheel using square-law damping (per comment
670 // in paper AIAA-2000-4303 - see header prologue comments). We might consider
671 // allowing for both square and linear damping force calculation. Also need to
672 // possibly give a "rebound damping factor" that differs from the compression
673 // case.
674
675 void FGLGear::ComputeVerticalStrutForce(void)
676 {
677   double springForce = 0;
678   double dampForce = 0;
679
680   springForce = -compressLength * kSpring;
681
682   if (compressSpeed >= 0.0) {
683
684     if (eDampType == dtLinear)   dampForce = -compressSpeed * bDamp;
685     else         dampForce = -compressSpeed * compressSpeed * bDamp;
686
687   } else {
688
689     if (eDampTypeRebound == dtLinear)
690       dampForce   = -compressSpeed * bDampRebound;
691     else
692       dampForce   =  compressSpeed * compressSpeed * bDampRebound;
693
694   }
695
696   StrutForce = min(springForce + dampForce, (double)0.0);
697
698   // The reaction force of the wheel is always normal to the ground
699   switch (eContactType) {
700   case ctBOGEY:
701     // Project back the strut force in the local coordinate frame of the ground
702     vFn(eX) = StrutForce / (mTGear.Transposed()*vGroundNormal)(eZ);
703     break;
704   case ctSTRUCTURE:
705     vFn(eX) = -StrutForce;
706     break;
707   }
708
709   // Remember these values for reporting
710   MaximumStrutForce = max(MaximumStrutForce, fabs(StrutForce));
711   MaximumStrutTravel = max(MaximumStrutTravel, fabs(compressLength));
712 }
713
714 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
715
716 double FGLGear::GetGearUnitPos(void)
717 {
718   // hack to provide backward compatibility to gear/gear-pos-norm property
719   if( useFCSGearPos || FCS->GetGearPos() != 1.0 ) {
720     useFCSGearPos = true;
721     return FCS->GetGearPos();
722   }
723   return GearPos;
724 }
725
726 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727
728 void FGLGear::bind(void)
729 {
730   string property_name;
731   string base_property_name;
732   base_property_name = CreateIndexedPropertyName("gear/unit", GearNumber);
733   if (eContactType == ctBOGEY) {
734     property_name = base_property_name + "/slip-angle-deg";
735     fdmex->GetPropertyManager()->Tie( property_name.c_str(), &WheelSlip );
736     property_name = base_property_name + "/WOW";
737     fdmex->GetPropertyManager()->Tie( property_name.c_str(), &WOW );
738     property_name = base_property_name + "/wheel-speed-fps";
739     fdmex->GetPropertyManager()->Tie( property_name.c_str(), (FGLGear*)this,
740                           &FGLGear::GetWheelRollVel);
741     property_name = base_property_name + "/z-position";
742     fdmex->GetPropertyManager()->Tie( property_name.c_str(), (FGForce*)this,
743                           &FGForce::GetLocationZ, &FGForce::SetLocationZ);
744     property_name = base_property_name + "/compression-ft";
745     fdmex->GetPropertyManager()->Tie( property_name.c_str(), &compressLength );
746     property_name = base_property_name + "/side_friction_coeff";
747     fdmex->GetPropertyManager()->Tie( property_name.c_str(), &FCoeff );
748
749     property_name = base_property_name + "/static_friction_coeff";
750     fdmex->GetPropertyManager()->Tie( property_name.c_str(), &staticFCoeff );
751
752     if (eSteerType == stCaster) {
753       property_name = base_property_name + "/steering-angle-rad";
754       fdmex->GetPropertyManager()->Tie( property_name.c_str(), &SteerAngle );
755     }
756   }
757
758   if( isRetractable ) {
759     property_name = base_property_name + "/pos-norm";
760     fdmex->GetPropertyManager()->Tie( property_name.c_str(), &GearPos );
761   }
762 }
763
764 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
765
766 void FGLGear::Report(ReportType repType)
767 {
768   if (fabs(TakeoffDistanceTraveled) < 0.001) return; // Don't print superfluous reports
769
770   switch(repType) {
771   case erLand:
772     cout << endl << "Touchdown report for " << name << " (WOW at time: "
773          << fdmex->GetState()->Getsim_time() << " seconds)" << endl;
774     cout << "  Sink rate at contact:  " << SinkRate                << " fps,    "
775                                 << SinkRate*0.3048          << " mps"     << endl;
776     cout << "  Contact ground speed:  " << GroundSpeed*.5925       << " knots,  "
777                                 << GroundSpeed*0.3048       << " mps"     << endl;
778     cout << "  Maximum contact force: " << MaximumStrutForce       << " lbs,    "
779                                 << MaximumStrutForce*4.448  << " Newtons" << endl;
780     cout << "  Maximum strut travel:  " << MaximumStrutTravel*12.0 << " inches, "
781                                 << MaximumStrutTravel*30.48 << " cm"      << endl;
782     cout << "  Distance traveled:     " << LandingDistanceTraveled        << " ft,     "
783                                 << LandingDistanceTraveled*0.3048  << " meters"  << endl;
784     LandingReported = true;
785     break;
786   case erTakeoff:
787     cout << endl << "Takeoff report for " << name << " (Liftoff at time: "
788          << fdmex->GetState()->Getsim_time() << " seconds)" << endl;
789     cout << "  Distance traveled:                " << TakeoffDistanceTraveled
790          << " ft,     " << TakeoffDistanceTraveled*0.3048  << " meters"  << endl;
791     cout << "  Distance traveled (over 50'):     " << TakeoffDistanceTraveled50ft
792          << " ft,     " << TakeoffDistanceTraveled50ft*0.3048 << " meters" << endl;
793     cout << "  [Altitude (ASL): " << fdmex->GetPropagate()->GetAltitudeASL() << " ft. / "
794          << fdmex->GetPropagate()->GetAltitudeASLmeters() << " m  | Temperature: "
795          << fdmex->GetAtmosphere()->GetTemperature() - 459.67 << " F / "
796          << RankineToCelsius(fdmex->GetAtmosphere()->GetTemperature()) << " C]" << endl;
797     cout << "  [Velocity (KCAS): " << fdmex->GetAuxiliary()->GetVcalibratedKTS() << "]" << endl;
798     TakeoffReported = true;
799     break;
800   case erNone:
801     break;
802   }
803 }
804
805 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806 //    The bitmasked value choices are as follows:
807 //    unset: In this case (the default) JSBSim would only print
808 //       out the normally expected messages, essentially echoing
809 //       the config files as they are read. If the environment
810 //       variable is not set, debug_lvl is set to 1 internally
811 //    0: This requests JSBSim not to output any messages
812 //       whatsoever.
813 //    1: This value explicity requests the normal JSBSim
814 //       startup messages
815 //    2: This value asks for a message to be printed out when
816 //       a class is instantiated
817 //    4: When this value is set, a message is displayed when a
818 //       FGModel object executes its Run() method
819 //    8: When this value is set, various runtime state variables
820 //       are printed out periodically
821 //    16: When set various parameters are sanity checked and
822 //       a message is printed out when they go out of bounds
823
824 void FGLGear::Debug(int from)
825 {
826   if (debug_lvl <= 0) return;
827
828   if (debug_lvl & 1) { // Standard console startup message output
829     if (from == 0) { // Constructor - loading and initialization
830       cout << "    " << sContactType << " " << name          << endl;
831       cout << "      Location: "         << vXYZn          << endl;
832       cout << "      Spring Constant:  " << kSpring       << endl;
833
834       if (eDampType == dtLinear)
835         cout << "      Damping Constant: " << bDamp << " (linear)" << endl;
836       else
837         cout << "      Damping Constant: " << bDamp << " (square law)" << endl;
838
839       if (eDampTypeRebound == dtLinear)
840         cout << "      Rebound Damping Constant: " << bDampRebound << " (linear)" << endl;
841       else 
842         cout << "      Rebound Damping Constant: " << bDampRebound << " (square law)" << endl;
843
844       cout << "      Dynamic Friction: " << dynamicFCoeff << endl;
845       cout << "      Static Friction:  " << staticFCoeff  << endl;
846       if (eContactType == ctBOGEY) {
847         cout << "      Rolling Friction: " << rollingFCoeff << endl;
848         cout << "      Steering Type:    " << sSteerType    << endl;
849         cout << "      Grouping:         " << sBrakeGroup   << endl;
850         cout << "      Max Steer Angle:  " << maxSteerAngle << endl;
851         cout << "      Retractable:      " << isRetractable  << endl;
852         cout << "      Relaxation Velocities:" << endl;
853         cout << "        Rolling:          " << RFRV << endl;
854         cout << "        Side:             " << SFRV << endl;
855       }
856     }
857   }
858   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
859     if (from == 0) cout << "Instantiated: FGLGear" << endl;
860     if (from == 1) cout << "Destroyed:    FGLGear" << endl;
861   }
862   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
863   }
864   if (debug_lvl & 8 ) { // Runtime state variables
865   }
866   if (debug_lvl & 16) { // Sanity checking
867   }
868   if (debug_lvl & 64) {
869     if (from == 0) { // Constructor
870       cout << IdSrc << endl;
871       cout << IdHdr << endl;
872     }
873   }
874 }
875
876 } // namespace JSBSim
877