FlightGear next
Gear.cpp
Go to the documentation of this file.
1
2#ifdef HAVE_CONFIG_H
3# include "config.h"
4#endif
5
6#include <simgear/debug/logstream.hxx>
7
8#include "Math.hpp"
9#include "BodyEnvironment.hpp"
10#include "Ground.hpp"
11#include "RigidBody.hpp"
12
13#include <cfloat>
14#include <simgear/bvh/BVHMaterial.hxx>
15#include <FDM/flight.hxx>
16#include "Gear.hpp"
17namespace yasim {
18static const float YASIM_PI = 3.14159265358979323846;
19static const float DEG2RAD = YASIM_PI / 180.0;
20static const float maxGroundBumpAmplitude=0.4;
21 //Amplitude can be positive and negative
22
23/* Sets <out> to unit vector along <v>, or all zeros if <v> is zero
24length. Returns length of <v>. */
25static float magnitudeUnit( const float* v, float* out)
26{
27 float magnitude = Math::mag3( v);
28 if ( magnitude) {
29 Math::mul3( 1 / magnitude, v, out);
30 }
31 else {
32 Math::zero3( out);
33 }
34 return magnitude;
35}
36
37GearVector::GearVector( float v0, float v1, float v2)
38{
39 set( v0, v1, v2);
40}
41
42void GearVector::set( float v0, float v1, float v2)
43{
44 v[0] = v0;
45 v[1] = v1;
46 v[2] = v2;
47 magnitude = magnitudeUnit( v, unit);
48}
49
50Gear::Gear()
51{
52 int i;
53 for(i=0; i<3; i++)
54 _pos[i] = _stuck[i] = 0;
55 _spring = 1;
56 _spring2 = 0;
57 _damp = 0;
58 _sfric = 0.8f;
59 _dfric = 0.7f;
60 _fric_spring = 0.005f; // Spring length = 5 mm
61 _brake = 0;
62 _rot = 0;
63 _initialLoad = 0;
64 _extension = 1;
65 _castering = false;
66 _frac = 0;
67 _ground_frictionFactor = 1;
68 _ground_rollingFriction = 0.02;
69 _ground_loadCapacity = 1e30;
70 _ground_loadResistance = 1e30;
71 _ground_isSolid = 1;
72 _ground_bumpiness = 0;
73 _onWater = 0;
74 _onSolid = 1;
75 _global_x = 0.0;
76 _global_y = 0.0;
77 _reduceFrictionByExtension = 0;
78 _spring_factor_not_planing = 1;
79 _speed_planing = 0;
80 _isContactPoint = 0;
81 _ignoreWhileSolving = 0;
82 _stiction = false;
83 _stiction_abs = false;
84
85 for(i=0; i<3; i++)
86 _global_ground[i] = _global_vel[i] = 0;
87 _global_ground[2] = 1;
88 _global_ground[3] = -1e3;
89
90 Math::zero3(_ground_trans);
91 Math::identity33(_ground_rot);
92
93 _wheelAxle.set(0, 1, 0);
94 _wheelRadius = 0;
95 _tyreRadius = 0;
96}
97
98void Gear::setCompression(float* compression)
99{
100 _cmpr.set( compression[0], compression[1], compression[2]);
101}
102
103void Gear::setWheelAxle(float* wheel_axle)
104{
105 _wheelAxle.set( wheel_axle[0], wheel_axle[1], wheel_axle[2]);
106 if (_wheelAxle.magnitude == 0) {
107 _wheelAxle.unit[1] = 1;
108 }
109}
110
111void Gear::setGlobalGround(double *global_ground, float* global_vel,
112 double globalX, double globalY,
113 const simgear::BVHMaterial *material, unsigned int body)
114{
115 int i;
116 double frictionFactor,rollingFriction,loadCapacity,loadResistance,bumpiness;
117 bool isSolid;
118
119 for(i=0; i<4; i++) _global_ground[i] = global_ground[i];
120 for(i=0; i<3; i++) _global_vel[i] = global_vel[i];
121
122 if (material) {
123 bool is_lake = (*material).solid_is_prop();
124 bool solid = (*material).get_solid();
125 if (is_lake && solid) { // on ice
126 frictionFactor = 0.3;
127 rollingFriction = 0.05;
128 bumpiness = 0.1;
129 } else {
130 frictionFactor = (*material).get_friction_factor();
131 rollingFriction = (*material).get_rolling_friction();
132 bumpiness = (*material).get_bumpiness();
133 }
134
135 loadCapacity = (*material).get_load_resistance();
136 loadResistance = (*material).get_load_resistance();
137 isSolid = (*material).get_solid();
138 } else {
139 // no material, assume solid
140 loadCapacity = DBL_MAX;
141 frictionFactor = 1.0;
142 rollingFriction = 0.02;
143 loadResistance = DBL_MAX;
144 bumpiness = 0.0;
145 isSolid = true;
146 }
147 _ground_frictionFactor = frictionFactor;
148 _ground_rollingFriction = rollingFriction;
149 _ground_loadCapacity = loadCapacity;
150 _ground_loadResistance = loadResistance;
151 _ground_bumpiness = bumpiness;
152 _ground_isSolid = isSolid;
153 _global_x = globalX;
154 _global_y = globalY;
155 _body_id = body;
156}
157
158void Gear::getGlobalGround(double* global_ground)
159{
160 for(int i=0; i<4; i++) global_ground[i] = _global_ground[i];
161}
162
163void Gear::getForce(float* force, float* contact)
164{
165 Math::set3(_force, force);
166 Math::set3(_contact, contact);
167}
168
169
170float Gear::getBumpAltitude()
171{
172 if (_ground_bumpiness<0.001) return 0.0;
173 double x = _global_x*0.1;
174 double y = _global_y*0.1;
175 x -= Math::floor(x);
176 y -= Math::floor(y);
177 x *= 2*YASIM_PI;
178 y *= 2*YASIM_PI;
179 //now x and y are in the range of 0..2pi
180 //we need a function, that is periodically on 2pi and gives some
181 //height. This is not very fast, but for a beginning.
182 //maybe this should be done by interpolating between some precalculated
183 //values
184 float h = Math::sin(x)+Math::sin(7*x)+Math::sin(8*x)+Math::sin(13*x);
185 h += Math::sin(2*y)+Math::sin(5*y)+Math::sin(9*y*x)+Math::sin(17*y);
186
187 return h*(1/8.)*_ground_bumpiness*maxGroundBumpAmplitude;
188}
189
190void Gear::integrate(float dt)
191{
192 // Slowly spin down wheel
193 if (_rollSpeed > 0) {
194 // The brake factor of 13.0 * dt was copied from JSBSim's FGLGear.cpp and seems to work reasonably.
195 // If more precise control is needed, then we need wheel mass and diameter parameters.
196 _rollSpeed -= (13.0 * dt + 1300 * _brake * dt);
197 if (_rollSpeed < 0) _rollSpeed = 0;
198 }
199 return;
200}
201
202
204 const float (&ground)[4],
205 const GearVector& compression,
206 const float (&wheel_pos)[3],
207 const GearVector (&wheel_axle),
208 float wheel_radius,
209 float tyre_radius,
210 std::function< float ()> bump_fn,
211 float (&o_contact)[3],
212 float& o_compression_distance_vertical,
213 float& o_compression_norm
214 )
215{
216 /* In this diagram we are looking at wheel end on.
217
218 N
219 \ S'
220 \ /
221 \ /
222 W--__
223 / --> wheel_axle |
224 / | G
225 S V
226
227 ------------------------- Ground plus tyre_radius
228
229 ========================= Ground
230
231 W is wheel_pos, position of wheel centre at maximum extension.
232
233 S is the lowest point of rim relative to ground. S' is the highest point
234 of the rim relative to the ground. So SWS' is a diameter across middle of
235 wheel. For most aircraft this will be vertical (relative to the aircraft),
236 but we don't require this; might be useful for a Bf109 aircraft.
237
238 N is position of wheel centre at maximum compression:
239 N = W + compression.
240
241 <wheel_axle> is wheel axle vector. Usually this is close to y axis, e.g. (
242 0, 1, 0).
243
244 G is ground normal, pointing downwards. Points p on ground satisfy:
245 G[:3].p = G3
246
247 Points p on (ground plus tyre_radius) satisfy:
248 G[:3].p = G3 + tyre_radius.
249
250 R is vector from W to S; to make S be lowest point on rim, this is radial
251 vector of wheel that is most closely aligned with G. We use:
252 R = - wheel_axle x G x wheel_axle
253
254 Distance of S below (ground plus tyre_radius), is:
255 a = S.G - ( G3 + tyre_radius)
256
257 If a < 0, the point of maximum extension is above ground and we return
258 false.
259
260 Otherwise, a >= 0 and the point of maximum extension is below ground, so
261 we are in contact with the ground and we compress the grear just enough to
262 make S be on ground plus tyre_radius. The tyre surface is tyre_radius from
263 S, so this ensures that the tyre will just touch the ground.
264
265 Compressing the gear means we move S (and W and S') a distance
266 compression_distance along vector <compression> (the vector from W to N),
267 and the projection of this movement along G must be -a. So:
268 ( compression_distance * compression) . G = -a
269 So:
270 compression_distance = -a / (compression . G)
271 */
272
273 float ground_unit[3];
274 magnitudeUnit( ground, ground_unit);
275
276 /* Find S, the lowest point on wheel. */
277 float S[3];
278 Math::set3( wheel_pos, S);
279
280 if (wheel_radius) {
281 /* Find radial wheel vector pointing closest to ground using two
282 cross-products: wheel_axle_unit x ground_unit x wheel_axle_unit */
283 float R[3];
284 Math::cross3( wheel_axle.unit, ground_unit, R);
285 Math::cross3( R, wheel_axle.unit, R);
286
287 /* Scale R to be length wheel_radius so it is vector from W to S, and
288 use it to find S. */
289 Math::unit3( R, R);
290 Math::mul3( wheel_radius, R, R);
291
292 /* Add R to S to get lowest point on wheel. */
293 Math::add3( S, R, S);
294 }
295
296 /* Calculate <a>, distance of S below ground. */
297 float a = Math::dot3( ground, S) - ( ground[3] - tyre_radius);
298 float bump_altitude = 0;
299 if ( a > -maxGroundBumpAmplitude) {
300 bump_altitude = bump_fn();
301 a -= bump_altitude;
302 }
303
304 bool ret = true;
305
306 if ( a < 0) {
307 /* S is above ground so we are not on ground, so set ret=false.
308
309 We force a=0 to pretend fully-extended gear is just on the ground, so
310 that o_contact will be set to the fully extended gear position. This
311 will be used by our caller to find /position/gear-agl-ft. */
312 ret = false;
313 a = 0;
314 }
315
316 /* Lowest part of wheel is below ground. */
317 o_compression_distance_vertical = a;
318
319 /* Find compression_norm. First we need to find compression_distance, the
320 distance to compress the gear so that S (which is below ground+tyre_radius)
321 would move to just touch ground+tyre_radius. We need to move gear further
322 depending on how perpendicular <compression> is to the ground. */
323 o_compression_norm = 0;
324 float compression_distance = 0;
325 float div = Math::dot3( compression.unit, ground_unit);
326 if (div) {
327 /* compression.unit points upwards, ground_unit points downwards, so
328 div is -ve. */
329 compression_distance = -a / div;
330 }
331
332 /* Set o_compression_norm. */
333 if ( compression.magnitude) {
334 o_compression_norm = compression_distance / compression.magnitude;
335 }
336 if (o_compression_norm > 1) {
337 o_compression_norm = 1;
338 }
339
340 /* Contact point on ground-plus-tyre-radius is S plus compression
341 vector. */
342 float delta[3];
343 Math::mul3( compression_distance, compression.unit, delta);
344 Math::add3( S, delta, o_contact);
345
346 if (ret)
347 {
348 /* If we are on the ground, verify that <o_contact> is tyre_radius
349 above ground; this can fail e.g. when resetting so for now we just
350 output a diagnostic rather than assert fail. */
351 //assert( fabs( Math::dot3( o_contact, ground) - (ground[3] - tyre_radius + bump_altitude)) < 0.001);
352 double s = Math::dot3( o_contact, ground) - (ground[3] - tyre_radius + bump_altitude);
353 if (fabs( s) > 0.001)
354 {
355 SG_LOG(SG_GENERAL, SG_ALERT, "<o_contact> is not tyre_radius above ground."
356 << " o_contact=" << o_contact
357 << " ground=" << ground
358 << " tyre_radius=" << tyre_radius
359 << " bump_altitude=" << bump_altitude
360 << " s=" << s
361 );
362 }
363 }
364
365 /* Correct for tyre_radius - need to move o_contact a distance tyre_radius
366 towards ground. */
367 if (tyre_radius) {
368 Math::mul3( tyre_radius, ground_unit, delta);
369 Math::add3( o_contact, delta, o_contact);
370 }
371
372 if (ret)
373 {
374 /* Verify that <o_contact> is on ground; this can fail e.g. when resetting so for now we
375 just output a diagnostic rather than assert fail. */
376 //assert( fabs( Math::dot3( o_contact, ground) - (ground[3] + bump_altitude)) < 0.001);
377 double s = Math::dot3( o_contact, ground) - (ground[3] + bump_altitude);
378 if ( fabs( s) > 0.001)
379 {
380 SG_LOG(SG_GENERAL, SG_ALERT, "<o_contact> is not on ground."
381 << " o_contact=" << o_contact
382 << " ground=" << ground
383 << " tyre_radius=" << tyre_radius
384 << " bump_altitude=" << bump_altitude
385 << " s=" << s
386 );
387 }
388 }
389
390 return ret;
391}
392
394 const float (&ground)[4],
395 const GearVector& compression,
396 const float (&pos)[3],
397 std::function< float ()> bump_fn,
398 float (&o_contact)[3],
399 float& o_compression_distance_vertical,
400 float& o_compression_norm
401 )
402{
403 // First off, make sure that the gear "tip" is below the ground.
404 // If it's not, there's no force.
405 float a = ground[3] - Math::dot3(pos, ground);
406 float BumpAltitude=0;
408 {
409 BumpAltitude = bump_fn();
410 a+=BumpAltitude;
411 }
412
413 if(a > 0) {
414 o_compression_distance_vertical = 0;
415 o_compression_norm = 0;
416 return false;
417 }
418
419 o_compression_distance_vertical = -a;
420
421 // Now a is the distance from the tip to ground, so make b the
422 // distance from the base to ground. We can get the fraction
423 // (0-1) of compression from a/(a-b). Note the minus sign -- stuff
424 // above ground is negative.
425 float tmp[3];
426 Math::add3(compression.v, pos, tmp);
427 float b = ground[3] - Math::dot3(tmp, ground)+BumpAltitude;
428
429 // Calculate the point of ground _contact.
430 if(b < 0)
431 o_compression_norm = 1;
432 else
433 o_compression_norm = a/(a-b);
434 for(int i=0; i<3; i++)
435 o_contact[i] = pos[i] + o_compression_norm * compression.v[i];
436 return true;
437}
438
439
440void Gear::calcForce(Ground *g_cb, RigidBody* body, State *s, float* v, float* rot)
441{
442 // Init the return values
443 int i;
444 for(i=0; i<3; i++) _force[i] = _contact[i] = 0;
445
446 // Don't bother if gear is retracted
447 if(_extension < 1)
448 {
449 _wow = 0;
450 _frac = 0;
451 return;
452 }
453
454 // Dont bother if we are in the "wrong" ground
455 if (!((_onWater&&!_ground_isSolid)||(_onSolid&&_ground_isSolid))) {
456 _wow = 0;
457 _frac = 0;
458 _compressDist = 0;
459 _rollSpeed = 0;
460 _casterAngle = 0;
461 return;
462 }
463
464 // The ground plane transformed to the local frame.
465 float ground[4];
466 s->planeGlobalToLocal(_global_ground, ground);
467
468 bool on_ground = gearCompression(
469 ground,
470 _cmpr,
471 _pos,
472 _wheelAxle,
473 _wheelRadius,
474 _tyreRadius,
475 [this] { return this->getBumpAltitude(); },
476 _contact,
477 _compressDist,
478 _frac
479 );
480 if (!on_ground) {
481 _wow = 0;
482 _frac = 0;
483 _compressDist = 0;
484 _casterAngle = 0;
485 return;
486 }
487
488 // The velocity of the contact patch transformed to local coordinates.
489 float glvel[3];
490 s->globalToLocal(_global_vel, glvel);
491
492 // Turn _cmpr into a unit vector and a magnitude
493 const float (&cmpr)[3] = _cmpr.unit;
494 const float &clen = _cmpr.magnitude;
495
496 // Now get the velocity of the point of contact
497 float cv[3];
498 body->pointVelocity(_contact, rot, cv);
499 Math::add3(cv, v, cv);
500 Math::sub3(cv, glvel, cv);
501
502 // Finally, we can start adding up the forces. First the spring
503 // compression. (note the clamping of _frac to 1):
504 _frac = (_frac > 1) ? 1 : _frac;
505
506 // Add the initial load to frac, but with continous transistion around 0
507 float frac_with_initial_load;
508 if (_frac>0.2 || _initialLoad==0.0)
509 frac_with_initial_load = _frac+_initialLoad;
510 else
511 frac_with_initial_load = (_frac+_initialLoad)
512 *_frac*_frac*3*25-_frac*_frac*_frac*2*125;
513
514 float fmag = clen * _spring *
515 (
516 frac_with_initial_load
517 + _spring2 * pow( frac_with_initial_load, 2)
518 );
519 if (_speed_planing>0)
520 {
521 float v = Math::mag3(cv);
522 if (v < _speed_planing)
523 {
524 float frac = v/_speed_planing;
525 fmag = fmag*_spring_factor_not_planing*(1-frac)+fmag*frac;
526 }
527 }
528 // Then the damping. Use the only the velocity into the ground
529 // (projection along "ground") projected along the compression
530 // axis. So Vdamp = ground*(ground dot cv) dot cmpr
531 float tmp[3];
532 Math::mul3(Math::dot3(ground, cv), ground, tmp);
533 float dv = Math::dot3(cmpr, tmp);
534 float damp = _damp * dv;
535 if(damp > fmag) damp = fmag; // can't pull the plane down!
536 if(damp < -fmag) damp = -fmag; // sanity
537
538 // The actual force applied is only the component perpendicular to
539 // the ground. Side forces come from velocity only.
540 _wow = (fmag - damp) * -Math::dot3(cmpr, ground);
541 Math::mul3(-_wow, ground, _force);
542
543 // Wheels are funky. Split the velocity along the ground plane
544 // into rolling and skidding components. Assuming small angles,
545 // we generate "forward" and "left" unit vectors (the compression
546 // goes "up") for the gear, make a "steer" direction from these,
547 // and then project it onto the ground plane. Project the
548 // velocity onto the ground plane too, and extract the "steer"
549 // component. The remainder is the skid velocity.
550
551 float gup[3]; // "up" unit vector from the ground
552 Math::set3(ground, gup);
553 Math::mul3(-1, gup, gup);
554
555 float xhat[] = {1,0,0};
556 float steer[3], skid[3];
557 Math::cross3(gup, xhat, skid); // up cross xhat =~ skid
558 Math::unit3(skid, skid); // == skid
559
560 Math::cross3(skid, gup, steer); // skid cross up == steer
561
562 if(_rot != 0) {
563 // Correct for a rotation
564 float srot = Math::sin(_rot);
565 float crot = Math::cos(_rot);
566 float tx = steer[0];
567 float ty = steer[1];
568 steer[0] = crot*tx + srot*ty;
569 steer[1] = -srot*tx + crot*ty;
570
571 tx = skid[0];
572 ty = skid[1];
573 skid[0] = crot*tx + srot*ty;
574 skid[1] = -srot*tx + crot*ty;
575 }
576
577 float vsteer = Math::dot3(cv, steer);
578 float vskid = Math::dot3(cv, skid);
579 float wgt = Math::dot3(_force, gup); // force into the ground
580
581 if(_castering) {
582 _rollSpeed = Math::sqrt(vsteer*vsteer + vskid*vskid);
583 // Don't modify caster angle when the wheel isn't moving,
584 // or else the angle will animate the "jitter" of a stopped
585 // gear.
586 if(_rollSpeed > 0.05)
587 _casterAngle = Math::atan2(vskid, vsteer);
588 return;
589 } else {
590 _rollSpeed = vsteer;
591 _casterAngle = _rot;
592 }
593
594 if (!_stiction) {
595 // original static friction function
596 calcForceWithoutStiction(wgt, steer, skid, cv, _force);
597 }
598 else {
599 // static friction feature based on spring attached to stuck point
600 calcForceWithStiction(g_cb, s, wgt, steer, skid, cv, _force);
601 }
602}
603
604void Gear::calcForceWithStiction(Ground *g_cb, State *s, float wgt, float steer[3], float skid[3], float *cv, float *force)
605{
606 float ffric[3];
607 if(_ground_isSolid) {
608 float stuckf[3];
609 double stuckd[3], lVel[3], aVel[3];
610 double body_xform[16];
611
612 // get AI body transform
613 g_cb->getBody(s->dt, body_xform, lVel, aVel, _body_id);
614 Math::set3(&body_xform[12], _ground_trans);
615
616 for (int i = 0; i < 3; i++) {
617 for (int j = 0; j < 3; j++) {
618 _ground_rot[i*3+j] = body_xform[i*4+j];
619 }
620 }
621
622 // translate and rotate stuck point in global coordinate space - compensate
623 // AI body movement
624 getStuckPoint(stuckd);
625
626 // convert global point to local aircraft reference coordinates
627 s->posGlobalToLocal(stuckd, stuckf);
628 calcFriction(stuckf, cv, steer, skid, wgt, ffric);
629 }
630 else {
631 calcFrictionFluid(cv, steer, skid, wgt, ffric);
632 }
633
634 // Phoo! All done. Add it up and get out of here.
635 Math::add3(ffric, force, force);
636}
637
638void Gear::calcForceWithoutStiction(float wgt, float steer[3], float skid[3], float *cv, float *force)
639{
640 // Get the velocities in the steer and skid directions
641 float vsteer, vskid;
642 vsteer = Math::dot3(cv, steer);
643 vskid = Math::dot3(cv, skid);
644
645 float tmp[3];
646 float fsteer,fskid;
647 if(_ground_isSolid) {
648 fsteer = (_brake * _ground_frictionFactor
649 +(1-_brake)*_ground_rollingFriction
650 )*calcFriction(wgt, vsteer);
651 fskid = calcFriction(wgt, vskid)*(_ground_frictionFactor);
652 }
653 else {
654 fsteer = calcFrictionFluid(wgt, vsteer)*_ground_frictionFactor;
655 fskid = 10*calcFrictionFluid(wgt, vskid)*_ground_frictionFactor;
656 //factor 10: floats have different drag in x and y.
657 }
658 if(vsteer > 0) fsteer = -fsteer;
659 if(vskid > 0) fskid = -fskid;
660
661 //reduce friction if wanted by _reduceFrictionByExtension
662 float factor = (1-_frac)*(1-_reduceFrictionByExtension)+_frac*1;
663 factor = Math::clamp(factor,0,1);
664 fsteer *= factor;
665 fskid *= factor;
666
667 // Phoo! All done. Add it up and get out of here.
668 Math::mul3(fsteer, steer, tmp);
669 Math::add3(tmp, force, force);
670
671 Math::mul3(fskid, skid, tmp);
672 Math::add3(tmp, force, force);
673}
674
675float Gear::calcFriction(float wgt, float v) //used on solid ground
676{
677 // How slow is stopped? 10 cm/second?
678 const float STOP = 0.1f;
679 const float iSTOP = 1.0f/STOP;
680 v = Math::abs(v);
681 if(v < STOP) return v*iSTOP * wgt * _sfric;
682 else return wgt * _dfric;
683}
684
685float Gear::calcFrictionFluid(float wgt, float v) //used on fluid ground
686{
687 // How slow is stopped? 1 cm/second?
688 const float STOP = 0.01f;
689 const float iSTOP = 1.0f/STOP;
690 v = Math::abs(v);
691 if(v < STOP) return v*iSTOP * wgt * _sfric;
692 else return wgt * _dfric*v*v*0.01;
693 //*0.01: to get _dfric of the same size than _dfric on solid
694}
695
696//used on solid ground
697void Gear::calcFriction(float *stuck, float *cv, float *steer, float *skid
698 , float wgt, float *force)
699{
700 // Calculate the gear's movement
701 float dgear[3];
702 Math::sub3(_contact, stuck, dgear);
703
704 // Get the movement in the steer and skid directions
705 float dsteer, dskid;
706 dsteer = Math::dot3(dgear, steer);
707 dskid = Math::dot3(dgear, skid);
708
709 // Get the velocities in the steer and skid directions
710 float vsteer, vskid;
711 vsteer = Math::dot3(cv, steer);
712 vskid = Math::dot3(cv, skid);
713
714 // If rolling and slipping are false, the gear is stopped
715 _rolling = false;
716 _slipping = false;
717 // Detect if it is slipping
718 if (Math::sqrt(dskid*dskid + dsteer*dsteer*(_brake/_sfric)) > _fric_spring)
719 {
720 // Turn on our ABS ;)
721 // This is off by default, because we don't want ABS on helicopters
722 // as their "wheels" should be locked all the time
723 if (_stiction && _stiction_abs && (Math::abs(dskid) < _fric_spring))
724 {
725 float bl;
726 bl = _sfric - _sfric * (Math::abs(dskid) / _fric_spring);
727 if (_brake > bl) _brake = bl;
728 }
729 else
730 _slipping = true;
731 }
732
733
734 float fric, fspring, fdamper, brake, tmp[3];
735 if (!_slipping)
736 {
737 // Calculate the steer force.
738 // Brake is limited between 0 and 1, wheel lock on 1.
739 brake = _brake > _sfric ? 1 : _brake/_sfric;
740 fspring = Math::abs((dsteer / _fric_spring) * wgt * _sfric);
741 // Set _rolling so the stuck point is updated
742 if ((Math::abs(dsteer) > _fric_spring) || (fspring > brake * wgt * _sfric))
743 {
744 _rolling = true;
745 fric = _ground_rollingFriction * wgt * _sfric; // Rolling
746 fric += _brake * wgt * _sfric * _ground_frictionFactor; // Brake
747 if (vsteer > 0) fric = -fric;
748 }
749 else // Stopped
750 {
751 fdamper = Math::abs(vsteer * wgt * _sfric);
752 fdamper *= ((dsteer * vsteer) > 0) ? 1 : -1;
753 fric = fspring + fdamper;
754 fric *= brake * _ground_frictionFactor
755 + (1 - brake) * _ground_rollingFriction;
756 if (dsteer > 0) fric = -fric;
757 }
758 Math::mul3(fric, steer, force);
759
760 // Calculate the skid force.
761 fspring = Math::abs((dskid / _fric_spring) * wgt * _sfric);
762 fdamper = Math::abs(vskid * wgt * _sfric);
763 fdamper *= ((dskid * vskid) > 0) ? 1 : -1;
764 fric = _ground_frictionFactor * (fspring + fdamper);
765 if (dskid > 0) fric = -fric;
766
767 Math::mul3(fric, skid, tmp);
768 Math::add3(force, tmp, force);
769
770 // The damper can add a lot of force,
771 // if it is to big, then it will slip
772 if (Math::mag3(force) > wgt * _sfric * _ground_frictionFactor)
773 {
774 _slipping = true;
775 _rolling = false;
776 }
777 }
778 if (_slipping)
779 {
780 // Get the direction of movement
781 float dir[3];
782 Math::unit3(dgear, dir);
783
784 // Calculate the steer force.
785 // brake is limited between 0 and 1, wheel lock on 1
786 brake = _brake > _dfric ? 1 : _brake/_dfric;
787 fric = wgt * _dfric * Math::abs(Math::dot3(dir, steer));
788 fric *= _ground_rollingFriction * (1 - brake)
789 + _ground_frictionFactor * brake;
790 if (vsteer > 0) fric = -fric;
791 Math::mul3(fric, steer, force);
792
793 // Calculate the skid force.
794 fric = wgt * _dfric * _ground_frictionFactor;
795 // Multiply by 1 when no brake, else reduce the turning component
796 fric *= (1 - brake) + Math::abs(Math::dot3(dir, skid)) * brake;
797 if (vskid > 0) fric = -fric;
798
799 Math::mul3(fric, skid, tmp);
800 Math::add3(force, tmp, force);
801 }
802
803 //reduce friction if wanted by _reduceFrictionByExtension
804 float factor = (1-_frac)*(1-_reduceFrictionByExtension)+_frac*1;
805 factor = Math::clamp(factor,0,1);
806 Math::mul3(factor, force, force);
807}
808
809//used on fluid ground
810void Gear::calcFrictionFluid(float *cv, float *steer, float *skid, float wgt, float *force)
811{
812 // How slow is stopped? 1 cm/second?
813 const float STOP = 0.01f;
814 const float iSTOP = 1.0f/STOP;
815 float vsteer, vskid;
816 vsteer = Math::dot3(cv, steer);
817 vskid = Math::dot3(cv, skid);
818
819 float tmp[3];
820 float fric;
821 // Calculate the steer force
822 float v = Math::abs(vsteer);
823 if(v < STOP) fric = v*iSTOP * wgt * _sfric;
824 else fric = wgt * _dfric*v*v*0.01;
825 //*0.01: to get _dfric of the same size than _dfric on solid
826 if (v > 0) fric = -fric;
827 Math::mul3(_ground_frictionFactor * fric, steer, force);
828
829 // Calculate the skid force
830 v = Math::abs(vskid);
831 if(v < STOP) fric = v*iSTOP * wgt * _sfric;
832 else fric = wgt * _dfric*v*v*0.01;
833 //*0.01: to get _dfric of the same size than _dfric on solid
834 if (v > 0) fric = -fric;
835 Math::mul3(10 * _ground_frictionFactor * fric, skid, tmp);
836 Math::add3(force, tmp, force);
837 //factor 10: floats have different drag in x and y.
838}
839
840// account for translation and rotation due to movements of AI body
841// doubles used since operations are taking place in global coordinate system
842void Gear::getStuckPoint(double *out)
843{
844 // get stuck point and add in AI body movement
845 Math::tmul33(_ground_rot, _stuck, out);
846 out[0] += _ground_trans[0];
847 out[1] += _ground_trans[1];
848 out[2] += _ground_trans[2];
849}
850
851void Gear::setStuckPoint(double *in)
852{
853 // Undo stuck point AI body movement and save as global coordinate
854 in[0] -= _ground_trans[0];
855 in[1] -= _ground_trans[1];
856 in[2] -= _ground_trans[2];
857 Math::vmul33(_ground_rot, in, _stuck);
858}
859
860void Gear::updateStuckPoint(State* s)
861{
862 float stuck[3];
863 double stuckd[3];
864
865 // get stuck point and update with AI body movement
866 getStuckPoint(stuckd);
867
868 // Convert stuck point to aircraft coordinates
869 s->posGlobalToLocal(stuckd, stuck);
870
871 // The ground plane transformed to the local frame.
872 float ground[4];
873 s->planeGlobalToLocal(_global_ground, ground);
874 float gup[3]; // "up" unit vector from the ground
875 Math::set3(ground, gup);
876 Math::mul3(-1, gup, gup);
877
878 float xhat[] = {1,0,0};
879 float skid[3], steer[3];
880 Math::cross3(gup, xhat, skid); // up cross xhat =~ skid
881 Math::unit3(skid, skid); // == skid
882 Math::cross3(skid, gup, steer); // skid cross up == steer
883
884 if(_rot != 0) {
885 // Correct for a rotation
886 float srot = Math::sin(_rot);
887 float crot = Math::cos(_rot);
888 float tx = steer[0];
889 float ty = steer[1];
890 steer[0] = crot*tx + srot*ty;
891 steer[1] = -srot*tx + crot*ty;
892
893 tx = skid[0];
894 ty = skid[1];
895
896 skid[0] = crot*tx + srot*ty;
897 skid[1] = -srot*tx + crot*ty;
898 }
899
900 if (_rolling)
901 {
902 // Calculate the gear's movement
903 float tmp[3];
904 Math::sub3(_contact, stuck, tmp);
905 // Get the movement in the skid and steer directions
906 float dskid, dsteer;
907 dskid = Math::dot3(tmp, skid);
908 dsteer = Math::dot3(tmp, steer);
909 // The movement is not always exactly on the steer axis
910 // so we should allow some empirical "slip" because of
911 // the wheel flexibility (max 5 degrees)
912 // FIXME: This angle should depend on the tire type/condition
913 // Is 5 degrees a good value?
914 float rad = (dskid / _fric_spring) * 5.0 * DEG2RAD;
915 dskid -= Math::abs(dsteer) * Math::tan(rad);
916
917 Math::mul3(dskid, skid, tmp);
918 Math::sub3(_contact, tmp, stuck);
919 }
920 else if (_slipping) {
921 Math::set3(_contact, stuck);
922 }
923
924 if (_rolling || _slipping)
925 {
926 // convert from local reference to global coordinates
927 s->posLocalToGlobal(stuck, stuckd);
928
929 // undo AI body rotations and translations and save stuck point in global coordinate space
930 setStuckPoint(stuckd);
931 }
932}
933
934
935}; // namespace yasim
936
#define i(x)
static const float maxGroundBumpAmplitude
Definition Gear.cpp:20
bool gearCompressionOld(const float(&ground)[4], const GearVector &compression, const float(&pos)[3], std::function< float()> bump_fn, float(&o_contact)[3], float &o_compression_distance_vertical, float &o_compression_norm)
Definition Gear.cpp:393
static const float YASIM_PI
Definition Gear.cpp:18
static const float DEG2RAD
Definition Gear.cpp:19
bool gearCompression(const float(&ground)[4], const GearVector &compression, const float(&wheel_pos)[3], const GearVector(&wheel_axle), float wheel_radius, float tyre_radius, std::function< float()> bump_fn, float(&o_contact)[3], float &o_compression_distance_vertical, float &o_compression_norm)
Definition Gear.cpp:203
static float magnitudeUnit(const float *v, float *out)
Definition Gear.cpp:25
const float R