FlightGear next
Model.cpp
Go to the documentation of this file.
1
2#ifdef HAVE_CONFIG_H
3# include "config.h"
4#endif
5
6#include "Airplane.hpp"
7#include "Atmosphere.hpp"
8#include "Thruster.hpp"
9#include "Math.hpp"
10#include "RigidBody.hpp"
11#include "Integrator.hpp"
12#include "Propeller.hpp"
13#include "PistonEngine.hpp"
14#include "Gear.hpp"
15#include "Hook.hpp"
16#include "Launchbar.hpp"
17#include "Surface.hpp"
18#include "Rotor.hpp"
19#include "Rotorpart.hpp"
20#include "Hitch.hpp"
21#include "Glue.hpp"
22#include "Ground.hpp"
23
24#include "Model.hpp"
25namespace yasim {
26
27#if 0
28void printState(State* s)
29{
30 State tmp = *s;
31 Math::vmul33(tmp.orient, tmp.v, tmp.v);
32 Math::vmul33(tmp.orient, tmp.acc, tmp.acc);
33 Math::vmul33(tmp.orient, tmp.rot, tmp.rot);
34 Math::vmul33(tmp.orient, tmp.racc, tmp.racc);
35
36 printf("\nNEW STATE (LOCAL COORDS)\n");
37 printf("pos: %10.2f %10.2f %10.2f\n", tmp.pos[0], tmp.pos[1], tmp.pos[2]);
38 printf("o: ");
39 int i;
40 for(i=0; i<3; i++) {
41 if(i != 0) printf(" ");
42 printf("%6.2f %6.2f %6.2f\n",
43 tmp.orient[3*i+0], tmp.orient[3*i+1], tmp.orient[3*i+2]);
44 }
45 printf("v: %6.2f %6.2f %6.2f\n", tmp.v[0], tmp.v[1], tmp.v[2]);
46 printf("acc: %6.2f %6.2f %6.2f\n", tmp.acc[0], tmp.acc[1], tmp.acc[2]);
47 printf("rot: %6.2f %6.2f %6.2f\n", tmp.rot[0], tmp.rot[1], tmp.rot[2]);
48 printf("rac: %6.2f %6.2f %6.2f\n", tmp.racc[0], tmp.racc[1], tmp.racc[2]);
49}
50#endif
51
52Model::Model(Airplane* parent) : _parent(parent)
53{
54 _integrator.setBody(&_body);
55 _integrator.setEnvironment(this);
56 // Default value of 30 Hz
57 _integrator.setInterval(1.0f/30.0f);
58
59 _ground_cb = new Ground();
60
61 _modelN = fgGetNode("/fdm/yasim/forces", true);
62 _fAeroXN = _modelN->getNode("f-x-drag", true);
63 _fAeroYN = _modelN->getNode("f-y-side", true);
64 _fAeroZN = _modelN->getNode("f-z-lift", true);
65 _fGravXN = _modelN->getNode("gravity-x", true);
66 _fGravYN = _modelN->getNode("gravity-y", true);
67 _fGravZN = _modelN->getNode("gravity-z", true);
68 _fSumXN = _modelN->getNode("f-sum-x", true);
69 _fSumYN = _modelN->getNode("f-sum-y", true);
70 _fSumZN = _modelN->getNode("f-sum-z", true);
71
72 _gefxN = fgGetNode("/fdm/yasim/debug/ground-effect/ge-f-x", true);
73 _gefyN = fgGetNode("/fdm/yasim/debug/ground-effect/ge-f-y", true);
74 _gefzN = fgGetNode("/fdm/yasim/debug/ground-effect/ge-f-z", true);
75 _wgdistN = fgGetNode("/fdm/yasim/debug/ground-effect/wing-gnd-dist", true);
76}
77
78Model::~Model()
79{
80 // FIXME: who owns these things? Need a policy
81 delete _ground_cb;
82 delete _hook;
83 delete _launchbar;
84 for(int i=0; i<_hitches.size();i++)
85 delete (Hitch*)_hitches.get(i);
86
87}
88
89void Model::getThrust(float* out) const
90{
91 float tmp[3];
92 out[0] = out[1] = out[2] = 0;
93 for(int i=0; i<_thrusters.size(); i++) {
94 Thruster* t = (Thruster*)_thrusters.get(i);
95 t->getThrust(tmp);
96 Math::add3(tmp, out, out);
97 }
98}
99
100void Model::initIteration()
101{
102 // Precompute torque and angular momentum for the thrusters
103 Math::zero3(_torque);
104 Math::zero3(_gyro);
105
106 // Need a local altitude for the wind calculation
107 float lground[4];
108 _s->planeGlobalToLocal(_global_ground, lground);
109 float alt = Math::abs(lground[3]);
110
111 for(int i=0; i<_thrusters.size(); i++) {
112 Thruster* t = (Thruster*)_thrusters.get(i);
113 // Get the wind velocity at the thruster location
114 float pos[3], v[3];
115 t->getPosition(pos);
116 localWind(pos, _s, v, alt);
117
118 t->setWind(v);
119 t->setAtmosphere(_atmo);
120 t->integrate(_integrator.getInterval());
121
122 t->getTorque(v);
123 Math::add3(v, _torque, _torque);
124
125 t->getGyro(v);
126 Math::add3(v, _gyro, _gyro);
127 }
128
129 // Displace the turbulence coordinates according to the local wind.
130 if(_turb) {
131 float toff[3];
132 Math::mul3(_integrator.getInterval(), _wind, toff);
133 _turb->offset(toff);
134 }
135
136 for(int i=0; i<_gears.size(); i++) {
137 Gear* g = (Gear*)_gears.get(i);
138 g->integrate(_integrator.getInterval());
139 }
140
141 for(int i=0; i<_hitches.size(); i++) {
142 Hitch* h = (Hitch*)_hitches.get(i);
143 h->integrate(_integrator.getInterval());
144 }
145}
146
147// This function initializes some variables for the rotor calculation
148// Furthermore it integrates in "void Rotorpart::inititeration
149// (float dt,float *rot)" the "rotor orientation" by omega*dt for the
150// 3D-visualization of the heli only. and it compensates the rotation
151// of the fuselage. The rotor does not follow the rotation of the fuselage.
152// Therefore its rotation must be subtracted from the orientation of the
153// rotor.
154// Maik
155void Model::initRotorIteration()
156{
157 float dt = _integrator.getInterval();
158 float lrot[3];
159 if (!_rotorgear.isInUse()) return;
160 Math::vmul33(_s->orient, _s->rot, lrot);
161 Math::mul3(dt,lrot,lrot);
162 _rotorgear.initRotorIteration(lrot,dt);
163}
164
165void Model::iterate()
166{
167 initIteration();
168 initRotorIteration();
169 _body.recalc(); // FIXME: amortize this, somehow
170 _integrator.calcNewInterval();
171}
172
173void Model::setState(State* s)
174{
175 _integrator.setState(s);
176 _s = _integrator.getState();
177}
178
179
180void Model::setGroundCallback(Ground* ground_cb)
181{
182 delete _ground_cb;
183 _ground_cb = ground_cb;
184}
185
186void Model::setGroundEffect(const float* pos, float span, float mul)
187{
188 Math::set3(pos, _geRefPoint);
189 _wingSpan = span;
190 _groundEffect = mul;
191}
192
193void Model::updateGround(State* s)
194{
195 float dummy[3];
196 unsigned int body;
197 _ground_cb->getGroundPlane(s->pos, _global_ground, dummy, body);
198
199 int i;
200 // The landing gear
201 for(i=0; i<_gears.size(); i++) {
202 Gear* g = (Gear*)_gears.get(i);
203
204 // Get the point of ground contact
205 float pos[3];
206 g->getContact(pos);
207
208 // Transform the local coordinates of the contact point to
209 // global coordinates.
210 double pt[3];
211 s->posLocalToGlobal(pos, pt);
212
213 // Ask for the ground plane in the global coordinate system
214 double global_ground[4];
215 float global_vel[3];
216 const simgear::BVHMaterial* material;
217 _ground_cb->getGroundPlane(pt, global_ground, global_vel, &material, body);
218 g->setGlobalGround(global_ground, global_vel, pt[0], pt[1], material, body);
219 }
220
221 for(i=0; i<_hitches.size(); i++) {
222 Hitch* h = (Hitch*)_hitches.get(i);
223
224 // Get the point of interest
225 float pos[3];
226 h->getPosition(pos);
227
228 // Transform the local coordinates of the contact point to
229 // global coordinates.
230 double pt[3];
231 s->posLocalToGlobal(pos, pt);
232
233 // Ask for the ground plane in the global coordinate system
234 double global_ground[4];
235 float global_vel[3];
236 _ground_cb->getGroundPlane(pt, global_ground, global_vel, body);
237 h->setGlobalGround(global_ground, global_vel);
238 }
239
240 for(i=0; i<_rotorgear.getRotors()->size(); i++) {
241 Rotor* r = (Rotor*)_rotorgear.getRotors()->get(i);
242 r->findGroundEffectAltitude(_ground_cb,s);
243 }
244
245 // The arrester hook
246 if(_hook) {
247 double pt[3];
248 _hook->getTipGlobalPosition(s, pt);
249 double global_ground[4];
250 _ground_cb->getGroundPlane(pt, global_ground, dummy, body);
251 _hook->setGlobalGround(global_ground);
252 }
253
254 // The launchbar/holdback
255 if(_launchbar) {
256 double pt[3];
257 _launchbar->getTipGlobalPosition(s, pt);
258 double global_ground[4];
259 _ground_cb->getGroundPlane(pt, global_ground, dummy, body);
260 _launchbar->setGlobalGround(global_ground);
261 }
262}
263
264void Model::calcForces(State* s)
265{
266 // Add in the pre-computed stuff. These values aren't part of the
267 // Runge-Kutta integration (they don't depend on position or
268 // velocity), and are therefore constant across the four calls to
269 // calcForces. They get computed before we begin the integration
270 // step.
271 _body.setGyro(_gyro);
272 _body.setTorque(_torque);
273 int i,j;
274 for(i=0; i<_thrusters.size(); i++) {
275 Thruster* t = (Thruster*)_thrusters.get(i);
276 float thrust[3], pos[3];
277 t->getThrust(thrust);
278 t->getPosition(pos);
279 _body.addForce(pos, thrust);
280 }
281
282 // Get a ground plane in local coordinates. The first three
283 // elements are the normal vector, the final one is the distance
284 // from the local origin along that vector to the ground plane
285 // (negative for objects "above" the ground)
286 float ground[4];
287 s->planeGlobalToLocal(_global_ground, ground);
288 float alt = Math::abs(ground[3]);
289
290 // Gravity, convert to a force, then to local coordinates
291 float grav[3];
292 Glue::geodUp(s->pos, grav);
293 Math::mul3(-9.8f * _body.getTotalMass(), grav, grav);
294 Math::vmul33(s->orient, grav, grav);
295 _body.addForce(grav);
296
297 // Do each surface, remembering that the local velocity at each
298 // point is different due to rotation.
299 float faero[3] {0,0,0};
300 if (!_surfaces.empty()) {
301 // approx mach number for aircraft (instead of per surface)
302 float vs[3] {0,0,0}, pos[3] {0,0,0};
303 localWind(pos, s, vs, alt);
304 float mach = _atmo.machFromSpeed(Math::mag3(vs));
305 for (i=0; i<_surfaces.size(); i++) {
306 Surface* sf = (Surface*)_surfaces.get(i);
307 // Vsurf = wind - velocity + (rot cross (cg - pos))
308 sf->getPosition(pos);
309 localWind(pos, s, vs, alt);
310
311 float force[3], torque[3];
312 sf->calcForce(vs, _atmo.getDensity(), mach, force, torque);
313 Math::add3(faero, force, faero);
314
315 _body.addForce(pos, force);
316 _body.addTorque(torque);
317 }
318 }
319 for (j=0; j<_rotorgear.getRotors()->size();j++)
320 {
321 Rotor* r = (Rotor *)_rotorgear.getRotors()->get(j);
322 float vs[3], pos[3];
323 r->getPosition(pos);
324 localWind(pos, s, vs, alt);
325 r->calcLiftFactor(vs, _atmo.getDensity(), s);
326 float tq=0;
327 // total torque of rotor (scalar) for calculating new rotor rpm
328
329 for(i=0; i<r->_rotorparts.size(); i++) {
330 float torque_scalar=0;
331 Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(i);
332
333 // Vsurf = wind - velocity + (rot cross (cg - pos))
334 float vs[3], pos[3];
335 rp->getPosition(pos);
336 localWind(pos, s, vs, alt,true);
337
338 float force[3], torque[3];
339 rp->calcForce(vs, _atmo.getDensity(), force, torque, &torque_scalar);
340 tq+=torque_scalar;
341 rp->getPositionForceAttac(pos);
342
343 _body.addForce(pos, force);
344 _body.addTorque(torque);
345 }
346 r->setTorque(tq);
347 }
348 if (_rotorgear.isInUse())
349 {
350 float torque[3];
351 _rotorgear.calcForces(torque);
352 _body.addTorque(torque);
353 }
354
355 // Account for ground effect by multiplying the vertical force
356 // component by an amount linear with the fraction of the wingspan
357 // above the ground.
358 if ((_wingSpan != 0) && (_groundEffect != 0 ))
359 {
360 // distance between ground and wing ref. point
361 float dist = ground[3] - Math::dot3(ground, _geRefPoint);
362 float fz = 0;
363 float geForce[3] = {0, 0, 0};
364 if(dist > 0 && dist < _wingSpan) {
365 fz = Math::dot3(faero, ground);
366 fz *= (_wingSpan - dist) / _wingSpan;
367 fz *= _groundEffect;
368 Math::mul3(fz, ground, geForce);
369 _body.addForce(geForce);
370 }
371 if (_modelN != 0) {
372 _gefxN->setFloatValue(geForce[0]);
373 _gefyN->setFloatValue(geForce[1]);
374 _gefzN->setFloatValue(geForce[2]);
375 _wgdistN->setFloatValue(dist);
376 }
377 }
378 if (_modelN != 0) {
379 _fAeroXN->setFloatValue(faero[0]);
380 _fAeroYN->setFloatValue(faero[1]);
381 _fAeroZN->setFloatValue(faero[2]);
382 _fGravXN->setFloatValue(grav[0]);
383 _fGravYN->setFloatValue(grav[1]);
384 _fGravZN->setFloatValue(grav[2]);
385 _fSumXN->setFloatValue(faero[0]+grav[0]);
386 _fSumYN->setFloatValue(faero[1]+grav[1]);
387 _fSumZN->setFloatValue(faero[2]+grav[2]);
388 }
389 // Convert the velocity and rotation vectors to local coordinates
390 float lrot[3], lv[3];
391 Math::vmul33(s->orient, s->rot, lrot);
392 Math::vmul33(s->orient, s->v, lv);
393
394 // The landing gear
395 for(i=0; i<_gears.size(); i++) {
396 float force[3], contact[3];
397 Gear* g = (Gear*)_gears.get(i);
398
399 g->calcForce(_ground_cb, &_body, s, lv, lrot);
400 g->getForce(force, contact);
401 _body.addForce(contact, force);
402 }
403
404 // The arrester hook
405 if(_hook) {
406 _hook->calcForce(_ground_cb, &_body, s, lv, lrot);
407 float force[3], contact[3];
408 _hook->getForce(force, contact);
409 _body.addForce(contact, force);
410 }
411
412 // The launchbar/holdback
413 if(_launchbar) {
414 _launchbar->calcForce(_ground_cb, &_body, s, lv, lrot);
415 float forcelb[3], contactlb[3], forcehb[3], contacthb[3];
416 _launchbar->getForce(forcelb, contactlb, forcehb, contacthb);
417 _body.addForce(contactlb, forcelb);
418 _body.addForce(contacthb, forcehb);
419 }
420
421 // The hitches
422 for(i=0; i<_hitches.size(); i++) {
423 float force[3], contact[3];
424 Hitch* h = (Hitch*)_hitches.get(i);
425 h->calcForce(_ground_cb,&_body, s);
426 h->getForce(force, contact);
427 _body.addForce(contact, force);
428 }
429}
430void Model::newState(State* s)
431{
432 _s = s;
433
434 // Some simple collision detection
435 float min = 1e8;
436 int i;
437 for(i=0; i<_gears.size(); i++) {
438 Gear* g = (Gear*)_gears.get(i);
439
440 if (!g->getSubmergable())
441 {
442 // Get the point of ground contact
443 float pos[3];
444 g->getContact(pos);
445
446 // The plane transformed to local coordinates.
447 double global_ground[4];
448 g->getGlobalGround(global_ground);
449 float ground[4];
450 s->planeGlobalToLocal(global_ground, ground);
451 float dist = ground[3] - Math::dot3(pos, ground);
452
453 // Find the lowest one
454 if(dist < min)
455 min = dist;
456 }
457 g->updateStuckPoint(s);
458 }
459 _agl = min;
460 if(_agl < -1) // Allow for some integration slop
461 _crashed = true;
462}
463
464// Calculates the airflow direction at the given point and for the
465// specified aircraft velocity.
466void Model::localWind(const float* pos, const yasim::State* s, float* out, float alt, bool is_rotor)
467{
468 float tmp[3], lwind[3], lrot[3], lv[3];
469
470 // Get a global coordinate for our local position, and calculate
471 // turbulence.
472 if(_turb) {
473 double gpos[3]; float up[3];
474 Math::tmul33(s->orient, pos, tmp);
475 for(int i=0; i<3; i++) {
476 gpos[i] = s->pos[i] + tmp[i];
477 }
478 Glue::geodUp(gpos, up);
479 _turb->getTurbulence(gpos, alt, up, lwind);
480 Math::add3(_wind, lwind, lwind);
481 } else {
482 Math::set3(_wind, lwind);
483 }
484
485 // Convert to local coordinates
486 Math::vmul33(s->orient, lwind, lwind);
487 Math::vmul33(s->orient, s->rot, lrot);
488 Math::vmul33(s->orient, s->v, lv);
489
490 _body.pointVelocity(pos, lrot, out); // rotational velocity
491 Math::mul3(-1, out, out); // (negated)
492 Math::add3(lwind, out, out); // + wind
493 Math::sub3(out, lv, out); // - velocity
494
495 //add the downwash of the rotors if it is not self a rotor
496 if (_rotorgear.isInUse()&&!is_rotor)
497 {
498 _rotorgear.getDownWash(pos,lv,tmp);
499 Math::add3(out,tmp, out); // + downwash
500 }
501}
502
503bool Model::isVersion(YASIM_VERSION version) const
504{
505 return _parent->isVersion(version);
506}
507
508bool Model::isVersionOrNewer(YASIM_VERSION version) const
509{
510 return _parent->isVersionOrNewer(version);
511}
512
513}; // namespace yasim
#define min(X, Y)
#define i(x)
State
Models an aircraft axis for purposes of trimming.
Definition FGTrimAxis.h:83
const double g(9.80665)
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27