]> git.mxchange.org Git - flightgear.git/blob - src/AIModel/AIBallistic.cxx
std:: namespace fixes, AIBase cleanup.
[flightgear.git] / src / AIModel / AIBallistic.cxx
1 // FGAIBallistic - FGAIBase-derived class creates a ballistic object
2 //
3 // Written by David Culp, started November 2003.
4 // - davidculp2@comcast.net
5 //
6 // With major additions by Mathias Froehlich & Vivian Meazza 2004-2008
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21
22 #ifdef HAVE_CONFIG_H
23 #  include <config.h>
24 #endif
25
26 #include <simgear/math/sg_random.h>
27 #include <simgear/math/sg_geodesy.hxx>
28 #include <simgear/scene/model/modellib.hxx>
29
30 #include <Scenery/scenery.hxx>
31
32 #include "AIBallistic.hxx"
33
34 #include <Main/util.hxx>
35 #include <Environment/gravity.hxx>
36
37 using namespace simgear;
38 using std::string;
39
40 const double FGAIBallistic::slugs_to_kgs = 14.5939029372;
41 const double FGAIBallistic::slugs_to_lbs = 32.1740485564;
42
43 FGAIBallistic::FGAIBallistic(object_type ot) :
44 FGAIBase(ot, false),
45 _height(0.0),
46 _speed(0),
47 _ht_agl_ft(0.0),
48 _azimuth(0.0),
49 _elevation(0.0),
50 _rotation(0.0),
51 hs(0),
52 _elapsed_time(0),
53 _aero_stabilised(false),
54 _drag_area(0.007),
55 _life_timer(0.0),
56 _buoyancy(0),
57 _wind(true),
58 _mass(0),
59 _random(false),
60 _load_resistance(0),
61 _solid(false),
62 _force_stabilised(false),
63 _slave_to_ac(false),
64 _slave_load_to_ac(false),
65 _contents_lb(0),
66 _report_collision(false),
67 _report_impact(false),
68 _external_force(false),
69 _report_expiry(false),
70 _impact_report_node(fgGetNode("/ai/models/model-impact", true)),
71 _old_height(0)
72
73 {
74     no_roll = false;
75 }
76
77 FGAIBallistic::~FGAIBallistic() {
78 }
79
80 void FGAIBallistic::readFromScenario(SGPropertyNode* scFileNode) {
81     if (!scFileNode){
82         return;
83     }
84
85     FGAIBase::readFromScenario(scFileNode);
86
87     //setPath(scFileNode->getStringValue("model", "Models/Geometry/rocket.ac")); 
88     setRandom(scFileNode->getBoolValue("random", false));
89     setAzimuth(scFileNode->getDoubleValue("azimuth", 0.0));
90     setElevation(scFileNode->getDoubleValue("elevation", 0));
91     setDragArea(scFileNode->getDoubleValue("eda", 0.007));
92     setLife(scFileNode->getDoubleValue("life", 900.0));
93     setBuoyancy(scFileNode->getDoubleValue("buoyancy", 0));
94     //setWind_from_east(scFileNode->getDoubleValue("wind_from_east", 0));
95     //setWind_from_north(scFileNode->getDoubleValue("wind_from_north", 0));
96     setWind(scFileNode->getBoolValue("wind", false));
97     setRoll(scFileNode->getDoubleValue("roll", 0.0));
98     setCd(scFileNode->getDoubleValue("cd", 0.029));
99     //setMass(scFileNode->getDoubleValue("mass", 0.007));
100     setWeight(scFileNode->getDoubleValue("weight", 0.25));
101     setStabilisation(scFileNode->getBoolValue("aero-stabilised", false));
102     setNoRoll(scFileNode->getBoolValue("no-roll", false));
103     setImpact(scFileNode->getBoolValue("impact", false));
104     setExpiry(scFileNode->getBoolValue("expiry", false));
105     setCollision(scFileNode->getBoolValue("collision", false));
106     setImpactReportNode(scFileNode->getStringValue("impact-reports"));
107     setName(scFileNode->getStringValue("name", "Rocket"));
108     setFuseRange(scFileNode->getDoubleValue("fuse-range", 0.0));
109     setSMPath(scFileNode->getStringValue("submodel-path", ""));
110     setSubID(scFileNode->getIntValue("SubID", 0));
111     setExternalForce(scFileNode->getBoolValue("external-force", false));
112     setForcePath(scFileNode->getStringValue("force-path", ""));
113     setForceStabilisation(scFileNode->getBoolValue("force-stabilised", false));
114     setXoffset(scFileNode->getDoubleValue("x-offset", 0.0));
115     setYoffset(scFileNode->getDoubleValue("y-offset", 0.0));
116     setZoffset(scFileNode->getDoubleValue("z-offset", 0.0));
117     setPitchoffset(scFileNode->getDoubleValue("pitch-offset", 0.0));
118     setRolloffset(scFileNode->getDoubleValue("roll-offset", 0.0));
119     setYawoffset(scFileNode->getDoubleValue("yaw-offset", 0.0));
120     setGroundOffset(scFileNode->getDoubleValue("ground-offset", 0.0));
121     setLoadOffset(scFileNode->getDoubleValue("load-offset", 0.0));
122     setSlaved(scFileNode->getBoolValue("slaved", false));
123     setSlavedLoad(scFileNode->getBoolValue("slaved-load", false));
124     setContentsPath(scFileNode->getStringValue("contents"));
125     setParentName(scFileNode->getStringValue("parent"));
126 }
127
128 bool FGAIBallistic::init(bool search_in_AI_path) {
129     FGAIBase::init(search_in_AI_path);
130     reinit();
131     return true;
132 }
133
134 void FGAIBallistic::reinit() {
135     _impact_reported = false;
136     _collision_reported = false;
137     _expiry_reported = false;
138
139     _impact_lat = 0;
140     _impact_lon = 0;
141     _impact_elev = 0;
142     _impact_hdg = 0;
143     _impact_pitch = 0;
144     _impact_roll = 0;
145     _impact_speed = 0;
146
147     invisible = false;
148
149     _elapsed_time += (sg_random() * 100);
150
151     _life_timer = 0;
152
153     props->setStringValue("material/name", "");
154     props->setStringValue("name", _name.c_str());
155     props->setStringValue("submodels/path", _path.c_str());
156
157     if (_slave_to_ac){
158         props->setStringValue("force/path", _force_path.c_str());
159         props->setStringValue("contents/path", _contents_path.c_str());
160     }
161
162     //cout << "init: name " << _name.c_str() << " _life_timer " << _life_timer 
163     //    << endl;
164
165     //if(_parent != ""){
166     //    setParentNode();
167     //}
168
169     //setParentNodes(_selected_ac);
170
171     //props->setStringValue("vector/path", _vector_path.c_str());
172
173     // start with high value so that animations don't trigger yet
174     _ht_agl_ft = 1e10;
175     hdg = _azimuth;
176     pitch = _elevation;
177     roll = _rotation;
178
179     Transform();
180
181     if(_parent != ""){
182         setParentNode();
183     }
184
185     setParentNodes(_selected_ac);
186
187     FGAIBase::reinit();
188 }
189
190 void FGAIBallistic::bind() {
191     //    FGAIBase::bind();
192
193     _tiedProperties.setRoot(props);
194     tie("sim/time/elapsed-sec",
195         SGRawValueMethods<FGAIBallistic,double>(*this,
196         &FGAIBallistic::_getTime, &FGAIBallistic::setTime));
197     //tie("mass-slug",
198     //    SGRawValueMethods<FGAIBallistic,double>(*this,
199     //    &FGAIBallistic::getMass));
200
201     tie("material/solid",
202         SGRawValuePointer<bool>(&_solid));
203     tie("altitude-agl-ft",
204         SGRawValuePointer<double>(&_ht_agl_ft));
205     tie("controls/slave-to-ac",
206         SGRawValueMethods<FGAIBallistic,bool>
207         (*this, &FGAIBallistic::getSlaved, &FGAIBallistic::setSlaved));
208     tie("controls/invisible",
209         SGRawValuePointer<bool>(&invisible));
210
211     if(_external_force || _slave_to_ac){
212         tie("controls/force_stabilized",
213             SGRawValuePointer<bool>(&_force_stabilised));
214         tie("position/global-x",
215             SGRawValueMethods<FGAIBase,double>(*this, &FGAIBase::_getCartPosX, 0));
216         tie("position/global-y",
217             SGRawValueMethods<FGAIBase,double>(*this, &FGAIBase::_getCartPosY, 0));
218         tie("position/global-z",
219             SGRawValueMethods<FGAIBase,double>(*this, &FGAIBase::_getCartPosZ, 0));
220         tie("velocities/vertical-speed-fps",
221             SGRawValuePointer<double>(&vs));
222         tie("velocities/true-airspeed-kt",
223             SGRawValuePointer<double>(&speed));
224         tie("velocities/horizontal-speed-fps",
225             SGRawValuePointer<double>(&hs));
226         tie("position/altitude-ft",
227             SGRawValueMethods<FGAIBase,double>(*this, &FGAIBase::_getElevationFt, &FGAIBase::_setAltitude));
228         tie("position/latitude-deg",
229             SGRawValueMethods<FGAIBase,double>(*this, &FGAIBase::_getLatitude, &FGAIBase::_setLatitude));
230         tie("position/longitude-deg",
231             SGRawValueMethods<FGAIBase,double>(*this, &FGAIBase::_getLongitude, &FGAIBase::_setLongitude));
232         tie("orientation/hdg-deg",
233             SGRawValuePointer<double>(&hdg));
234         tie("orientation/pitch-deg",
235             SGRawValuePointer<double>(&pitch));
236         tie("orientation/roll-deg",
237             SGRawValuePointer<double>(&roll));
238         tie("controls/slave-load-to-ac",
239             SGRawValueMethods<FGAIBallistic,bool>
240             (*this, &FGAIBallistic::getSlavedLoad, &FGAIBallistic::setSlavedLoad));
241         tie("position/load-offset",
242             SGRawValueMethods<FGAIBallistic,double>
243             (*this, &FGAIBallistic::getLoadOffset, &FGAIBallistic::setLoadOffset));
244         tie("load/distance-to-hitch-ft",
245             SGRawValueMethods<FGAIBallistic,double>
246             (*this, &FGAIBallistic::getDistanceToHitch));
247         tie("load/elevation-to-hitch-deg",
248             SGRawValueMethods<FGAIBallistic,double>
249             (*this, &FGAIBallistic::getElevToHitch));
250         tie("load/bearing-to-hitch-deg",
251             SGRawValueMethods<FGAIBallistic,double>
252             (*this, &FGAIBallistic::getBearingToHitch));
253         tie("material/load-resistance",
254         SGRawValuePointer<double>(&_load_resistance));
255     }
256
257 }
258
259 void FGAIBallistic::update(double dt)
260 {
261     FGAIBase::update(dt);
262
263     if (_slave_to_ac){
264         slaveToAC(dt);
265         Transform();
266     } else if (!invisible){
267         Run(dt);
268         Transform();
269     }
270
271 }
272
273 void FGAIBallistic::setAzimuth(double az) {
274
275     if (_random)
276         hdg = _azimuth = (az - 5 ) + (10 * sg_random());
277     else 
278         hdg = _azimuth = az;
279
280     //cout << _name << " init hdg " << hdg << " random " << _random << endl;
281 }
282
283 void FGAIBallistic::setElevation(double el) {
284     pitch = _elevation = el;
285 }
286
287 void FGAIBallistic::setRoll(double rl) {
288     roll = _rotation = rl;
289 }
290
291 void FGAIBallistic::setStabilisation(bool val) {
292     _aero_stabilised = val;
293 }
294
295 void FGAIBallistic::setForceStabilisation(bool val) {
296     _force_stabilised = val;
297 }
298
299 void FGAIBallistic::setNoRoll(bool nr) {
300     no_roll = nr;
301 }
302
303 void FGAIBallistic::setDragArea(double a) {
304     _drag_area = a;
305 }
306
307 void FGAIBallistic::setLife(double seconds) {
308
309     if (_random){
310         life = seconds * _randomness + (seconds * (1 -_randomness) * sg_random());
311         //cout << " set life " << life << endl;
312     } else
313         life = seconds;
314 }
315
316 void FGAIBallistic::setBuoyancy(double fpss) {
317     _buoyancy = fpss;
318 }
319
320 void FGAIBallistic::setWind_from_east(double fps) {
321     _wind_from_east = fps;
322 }
323
324 void FGAIBallistic::setWind_from_north(double fps) {
325     _wind_from_north = fps;
326 }
327
328 void FGAIBallistic::setWind(bool val) {
329     _wind = val;
330 }
331
332 void FGAIBallistic::setCd(double c) {
333     _Cd = c;
334 }
335
336 void FGAIBallistic::setMass(double m) {
337     _mass = m;
338 }
339
340 void FGAIBallistic::setWeight(double w) {
341     _weight_lb = w;
342 }
343
344 void FGAIBallistic::setRandomness(double r) {
345     _randomness = r;
346 }
347
348 void FGAIBallistic::setRandom(bool r) {
349     _random = r;
350 }
351
352 void FGAIBallistic::setImpact(bool i) {
353     _report_impact = i;
354 }
355
356 void FGAIBallistic::setCollision(bool c) {
357     _report_collision = c;
358 }
359
360 void FGAIBallistic::setExpiry(bool e) {
361     _report_expiry = e;
362 }
363
364 void FGAIBallistic::setExternalForce(bool f) {
365     _external_force = f;
366 }
367
368 void FGAIBallistic::setImpactReportNode(const string& path) {
369
370     if (!path.empty())
371         _impact_report_node = fgGetNode(path.c_str(), true);
372 }
373
374 void FGAIBallistic::setSMPath(const string& s) {
375     _path = s;
376     //cout << "submodel path " << _path << endl;
377 }
378
379 void FGAIBallistic::setFuseRange(double f) {
380     _fuse_range = f;
381 }
382
383 void FGAIBallistic::setSubID(int i) {
384     _subID = i;
385 }
386
387 void FGAIBallistic::setSubmodel(const string& s) {
388     _submodel = s;
389 }
390
391 void FGAIBallistic::setGroundOffset(double g) {
392     _ground_offset = g;
393 }
394
395 void FGAIBallistic::setLoadOffset(double l) {
396     _load_offset = l;
397 }
398
399 double FGAIBallistic::getLoadOffset() const {
400     return _load_offset;
401 }
402
403 void FGAIBallistic::setSlaved(bool s) {
404     _slave_to_ac = s;
405 }
406
407 void FGAIBallistic::setContentsPath(const string& path) {
408
409     _contents_path = path;
410
411     if (!path.empty()) {
412         _contents_node = fgGetNode(path.c_str(), true);
413     }
414 }
415
416 void FGAIBallistic::setContentsNode(SGPropertyNode_ptr node) {
417
418     if (node != 0) {
419         _contents_node = node;
420         _contents_path = _contents_node->getDisplayName();
421     }
422 }
423
424 void FGAIBallistic::setParentNodes(SGPropertyNode_ptr node) {
425
426     if (node != 0) {
427         _pnode = node;
428         _p_pos_node = _pnode->getChild("position", 0, true);
429         _p_lat_node = _p_pos_node->getChild("latitude-deg", 0, true);
430         _p_lon_node = _p_pos_node->getChild("longitude-deg", 0, true);
431         _p_alt_node = _p_pos_node->getChild("altitude-ft", 0, true);
432         _p_agl_node = _p_pos_node->getChild("altitude-agl-ft", 0, true);
433
434
435         _p_ori_node = _pnode->getChild("orientation", 0, true);
436         _p_pch_node = _p_ori_node->getChild("pitch-deg", 0, true);
437         _p_rll_node = _p_ori_node->getChild("roll-deg", 0, true);
438         _p_hdg_node = _p_ori_node->getChild("true-heading-deg",0, true);
439
440         _p_vel_node = _pnode->getChild("velocities", 0, true);
441         _p_spd_node = _p_vel_node->getChild("true-airspeed-kt", 0, true);
442     }
443
444 }
445
446 void FGAIBallistic::setParentPos() {
447
448     if (_pnode != 0) { 
449         //cout << "set parent pos" << endl;
450
451         double lat = _p_lat_node->getDoubleValue();
452         double lon = _p_lon_node->getDoubleValue();
453         double alt = _p_alt_node->getDoubleValue();
454
455         _parentpos.setLongitudeDeg(lon);
456         _parentpos.setLatitudeDeg(lat);
457         _parentpos.setElevationFt(alt);
458
459     }
460
461 }
462
463 bool FGAIBallistic::getSlaved() const {
464     return _slave_to_ac;
465 }
466
467 double FGAIBallistic::getMass() const {
468     return _mass;
469 }
470
471 double FGAIBallistic::getContents() {
472     if(_contents_node){
473         _contents_lb = _contents_node->getChild("level-lbs",0,1)->getDoubleValue();
474     }
475     return _contents_lb;
476 }
477
478 void FGAIBallistic::setContents(double c) {
479     if(_contents_node) 
480         _contents_lb = _contents_node->getChild("level-gal_us",0,1)->setDoubleValue(c);
481 }
482
483 void FGAIBallistic::setSlavedLoad(bool l) {
484     _slave_load_to_ac = l;
485 }
486
487 bool FGAIBallistic::getSlavedLoad() const {
488     return _slave_load_to_ac;
489 }
490
491 void FGAIBallistic::setForcePath(const string& p) {
492     _force_path = p;
493     if (!_force_path.empty()) {
494         SGPropertyNode *fnode = fgGetNode(_force_path.c_str(), 0, true );
495         _force_node = fnode->getChild("force-lb", 0, true);
496         _force_azimuth_node = fnode->getChild("force-azimuth-deg", 0, true);
497         _force_elevation_node = fnode->getChild("force-elevation-deg", 0, true);
498     }
499 }
500
501 bool FGAIBallistic::getHtAGL(double start){
502     const simgear::BVHMaterial* mat = 0;
503     if (getGroundElevationM(SGGeod::fromGeodM(pos, start),
504         _elevation_m, &mat)) {
505             const SGMaterial* material = dynamic_cast<const SGMaterial*>(mat);
506             _ht_agl_ft = pos.getElevationFt() - _elevation_m * SG_METER_TO_FEET;
507
508             if (material) {
509                 const std::vector<string>& names = material->get_names();
510                 _solid = material->get_solid();
511                 _load_resistance = material->get_load_resistance();
512                 _frictionFactor = material->get_friction_factor();
513
514                 if (!names.empty())
515                     props->setStringValue("material/name", names[0].c_str());
516                 else
517                     props->setStringValue("material/name", "");
518
519                 _mat_name = names[0];
520
521                 //cout << "material " << _mat_name 
522                 //<< " solid " << _solid 
523                 //<< " load " << _load_resistance
524                 //<< " frictionFactor " << _frictionFactor
525                 //<< endl;
526
527             }
528
529             return true;
530     } else {
531         return false;
532     }
533
534 }
535
536 double FGAIBallistic::getRecip(double az){
537     // calculate the reciprocal of the input azimuth 
538     if(az - 180 < 0){
539         return az + 180;
540     } else {
541         return az - 180; 
542     }
543 }
544
545 void FGAIBallistic::setPch(double e, double dt, double coeff){
546     double c = dt / (coeff + dt);
547     pitch = (e * c) + (pitch * (1 - c));
548 }
549
550 void FGAIBallistic::setBnk(double r, double dt, double coeff){
551     double c = dt / (coeff + dt);
552     roll = (r * c) + (roll * (1 - c));
553 }
554
555 void FGAIBallistic::setSpd(double s, double dt, double coeff){
556     double c = dt / (coeff + dt);
557     _speed = (s * c) + (_speed * (1 - c));
558 }
559
560 void FGAIBallistic::setHt(double h, double dt, double coeff){
561     double c = dt / (coeff + dt);
562     _height = (h * c) + (_height * (1 - c));
563 }
564
565 int FGAIBallistic::setHdg(double tgt_hdg, double dt, double coeff){
566     double recip = getRecip(hdg);
567     double c = dt / (coeff + dt);
568     //cout << "set heading " << tgt_hdg << endl;
569     //we need to ensure that we turn the short way to the new hdg
570     if (tgt_hdg < recip && tgt_hdg < hdg && hdg > 180) {
571         hdg = ((tgt_hdg + 360) * c) + (hdg * (1 - c));
572 //        cout << "case 1: right turn" << endl;
573     } else if (tgt_hdg > recip && tgt_hdg > hdg && hdg <= 180){
574         hdg = ((tgt_hdg - 360) * c) + (hdg * (1 - c));
575 //        cout << "case 2: left turn" << endl;
576     } else {
577         hdg = (tgt_hdg * c) + (hdg * (1 - c));
578 //        cout << "case 4: left turn" << endl;
579     }
580     return -1;
581 }
582
583 double  FGAIBallistic::getTgtXOffset() const {
584     return _tgt_x_offset;
585 }
586
587 double  FGAIBallistic::getTgtYOffset() const {
588     return _tgt_y_offset;
589
590
591 double  FGAIBallistic::getTgtZOffset() const {
592     return _tgt_z_offset;
593 }
594
595 void FGAIBallistic::setTgtXOffset(double x){
596     _tgt_x_offset = x;
597 }
598
599 void FGAIBallistic::setTgtYOffset(double y){
600     _tgt_y_offset = y;
601 }
602
603 void FGAIBallistic::setTgtZOffset(double z){
604     _tgt_z_offset = z;
605 }
606
607 void FGAIBallistic::slaveToAC(double dt){
608
609     if (invisible)
610         return;
611
612     double hdg, pch, rll;//, agl = 0;
613
614     if (_pnode != 0) {
615         setParentPos();
616         hdg = _p_hdg_node->getDoubleValue();
617         pch = _p_pch_node->getDoubleValue();
618         rll = _p_rll_node->getDoubleValue();
619 //        agl = _p_agl_node->getDoubleValue();
620         setOffsetPos(_parentpos, hdg, pch, rll);
621         setSpeed(_p_spd_node->getDoubleValue());
622     }else {
623         hdg = manager->get_user_heading();
624         pch = manager->get_user_pitch();
625         rll = manager->get_user_roll();
626 //        agl = manager->get_user_agl();
627         setOffsetPos(globals->get_aircraft_position(), hdg, pch, rll);
628         setSpeed(manager->get_user_speed());
629     }
630
631     pos.setLatitudeDeg(_offsetpos.getLatitudeDeg());
632     pos.setLongitudeDeg(_offsetpos.getLongitudeDeg());
633     pos.setElevationFt(_offsetpos.getElevationFt());
634     setHeading(hdg);
635     setPitch(pch + _pitch_offset);
636     setBank(rll + _roll_offset);
637     setOffsetVelocity(dt, pos);
638     setTime(0);
639
640     //update the mass (slugs)
641     _mass = (_weight_lb + getContents()) / slugs_to_lbs;
642
643     _impact_reported = false;
644
645     //cout << _name << " _mass "<<_mass <<" " << getContents() 
646     //<< " " << getContents() / slugs_to_lbs << " weight " << _weight_lb << endl;
647     //    cout << _name << " update hs " << hs << " vs " << vs << endl;
648 }
649
650 void FGAIBallistic::Run(double dt) {
651     _life_timer += dt;
652     
653     //_pass += 1;
654     //cout<<"AIBallistic run: name " << _name.c_str() 
655     //    << " dt " << dt <<  " _life_timer " << _life_timer << " pass " << _pass << endl;
656
657     // if life = -1 the object does not die
658     if (_life_timer > life && life != -1){
659
660         if (_report_expiry && !_expiry_reported && !_impact_reported && !_collision_reported){
661             //cout<<"AIBallistic run: name " << _name.c_str() << " expiry " 
662                 //<< " _life_timer " << _life_timer<< endl;
663             handle_expiry();
664         } else{
665             //cout<<"AIBallistic run: name " << _name.c_str() 
666             //    << " die " <<  " _life_timer " << _life_timer << endl;
667             setDie(true);
668         }
669
670         setTime(0);
671     }
672
673     //set the contents in the appropriate tank or other property in the parent to zero
674     setContents(0);
675
676     //randomise Cd by +- 10%
677     if (_random)
678         _Cd = _Cd * 0.90 + (0.10 * sg_random());
679
680     // Adjust Cd by Mach number. The equations are based on curves
681     // for a conventional shell/bullet (no boat-tail).
682     double Cdm;
683
684     if (Mach < 0.7)
685         Cdm = 0.0125 * Mach + _Cd;
686     else if (Mach < 1.2 )
687         Cdm = 0.3742 * pow(Mach, 2) - 0.252 * Mach + 0.0021 + _Cd;
688     else
689         Cdm = 0.2965 * pow(Mach, -1.1506) + _Cd;
690
691     //cout <<_name << " Mach " << Mach << " Cdm " << Cdm 
692     //    << " ballistic speed kts "<< speed <<  endl;
693
694     // drag = Cd * 0.5 * rho * speed * speed * drag_area;
695     // rho is adjusted for altitude in void FGAIBase::update,
696     // using Standard Atmosphere (sealevel temperature 15C)
697     // acceleration = drag/mass;
698     // adjust speed by drag
699     speed -= (Cdm * 0.5 * rho * speed * speed * _drag_area/_mass) * dt;
700
701     // don't let speed become negative
702     if ( speed < 0.0 )
703         speed = 0.0;
704
705 //    double speed_fps = speed * SG_KT_TO_FPS;
706
707     // calculate vertical and horizontal speed components
708     calcVSHS();
709
710     //resolve horizontal speed into north and east components:
711     //and convert horizontal speed (fps) to degrees per second
712     calcNE();
713
714     // if wind not required, set to zero
715     if (!_wind) {
716         _wind_from_north = 0;
717         _wind_from_east = 0;
718     } else {
719         _wind_from_north = manager->get_wind_from_north();
720         _wind_from_east = manager->get_wind_from_east();
721     }
722
723     //calculate velocity due to external force
724     double force_speed_north_deg_sec = 0;
725     double force_speed_east_deg_sec = 0;
726 //    double vs_force_fps = 0;
727     double hs_force_fps = 0;
728     double v_force_acc_fpss = 0;
729     double force_speed_north_fps = 0;
730     double force_speed_east_fps = 0;
731     double h_force_lbs = 0;
732     double normal_force_lbs = 0;
733     double normal_force_fpss = 0;
734     double static_friction_force_lbs = 0;
735     double dynamic_friction_force_lbs = 0;
736     double friction_force_speed_north_fps = 0;
737     double friction_force_speed_east_fps = 0;
738     double friction_force_speed_north_deg_sec = 0;
739     double friction_force_speed_east_deg_sec = 0;
740     double force_elevation_deg = 0;
741     double force_azimuth_deg  = 0;
742     double force_lbs = 0;
743
744     if (_external_force) {
745         //cout << _name << " external force " <<  hdg << " az " << _azimuth << endl;
746
747         SGPropertyNode *n = fgGetNode(_force_path.c_str(), true);
748         force_lbs            = n->getChild("force-lb", 0, true)->getDoubleValue();
749         force_elevation_deg  = n->getChild("force-elevation-deg", 0, true)->getDoubleValue();
750         force_azimuth_deg    = n->getChild("force-azimuth-deg", 0, true)->getDoubleValue();
751         
752         //resolve force into vertical and horizontal components:
753         double v_force_lbs = force_lbs * sin( force_elevation_deg * SG_DEGREES_TO_RADIANS );
754         h_force_lbs = force_lbs * cos( force_elevation_deg * SG_DEGREES_TO_RADIANS );
755
756         //ground interaction 
757         //we don't do this if impacts are calculated
758         if(!_report_impact){
759
760             if (getHtAGL(10000)){
761                 double deadzone = 0.1;
762
763                 if (_ht_agl_ft <= (0 + _ground_offset + deadzone) && _solid){
764                     normal_force_lbs = (_mass * slugs_to_lbs) - v_force_lbs;
765
766                     if ( normal_force_lbs < 0 )
767                         normal_force_lbs = 0;
768
769                     pos.setElevationFt(0 + _ground_offset);
770                     if (vs < 0) 
771                         vs = -vs * 0.5;
772
773                     // calculate friction
774                     // we assume a static Coefficient of Friction (mu) of 0.62 (wood on concrete)
775                     double mu = 0.62;
776
777                     static_friction_force_lbs = mu * normal_force_lbs * _frictionFactor;
778
779                     //adjust horizontal force. We assume that a speed of <= 5 fps is static 
780                     if (h_force_lbs <= static_friction_force_lbs && hs <= 5){
781                         h_force_lbs = hs = 0;
782                         _speed_north_fps = _speed_east_fps = 0;
783                     } else
784                         dynamic_friction_force_lbs = (static_friction_force_lbs * 0.95);
785
786                     //ignore wind when on the ground for now
787                     //TODO fix this
788                     _wind_from_north = 0;
789                     _wind_from_east = 0;
790
791                 }
792
793             }
794
795         } //endif
796
797         //acceleration = (force(lbsf)/mass(slugs))
798         v_force_acc_fpss = v_force_lbs/_mass;
799         normal_force_fpss = normal_force_lbs/_mass;
800         double h_force_acc_fpss = h_force_lbs/_mass;
801         double dynamic_friction_acc_fpss = dynamic_friction_force_lbs/_mass;
802
803         // velocity = acceleration * dt
804         hs_force_fps = h_force_acc_fpss * dt;
805         double friction_force_fps = dynamic_friction_acc_fpss * dt;
806
807         //resolve horizontal speeds into north and east components:
808         force_speed_north_fps   = cos(force_azimuth_deg * SG_DEGREES_TO_RADIANS) * hs_force_fps;
809         force_speed_east_fps    = sin(force_azimuth_deg * SG_DEGREES_TO_RADIANS) * hs_force_fps;
810
811         friction_force_speed_north_fps = cos(getRecip(hdg) * SG_DEGREES_TO_RADIANS) * friction_force_fps;
812         friction_force_speed_east_fps  = sin(getRecip(hdg) * SG_DEGREES_TO_RADIANS) * friction_force_fps;
813
814         // convert horizontal speed (fps) to degrees per second
815         force_speed_north_deg_sec = force_speed_north_fps / ft_per_deg_lat;
816         force_speed_east_deg_sec  = force_speed_east_fps / ft_per_deg_lon;
817
818         friction_force_speed_north_deg_sec = friction_force_speed_north_fps / ft_per_deg_lat;
819         friction_force_speed_east_deg_sec  = friction_force_speed_east_fps / ft_per_deg_lon;
820     }
821
822     // convert wind speed (fps) to degrees lat/lon per second
823     double wind_speed_from_north_deg_sec = _wind_from_north / ft_per_deg_lat;
824     double wind_speed_from_east_deg_sec  = _wind_from_east / ft_per_deg_lon;
825
826     //recombine the horizontal velocity components
827     hs = sqrt(((_speed_north_fps + force_speed_north_fps + friction_force_speed_north_fps) 
828         * (_speed_north_fps + force_speed_north_fps + friction_force_speed_north_fps))
829         + ((_speed_east_fps + force_speed_east_fps + friction_force_speed_east_fps) 
830         * (_speed_east_fps + force_speed_east_fps + friction_force_speed_east_fps)));
831
832     if (hs <= 0.00001)
833         hs = 0;
834
835     // adjust vertical speed for acceleration of gravity, buoyancy, and vertical force
836     double gravity = SG_METER_TO_FEET * (Environment::Gravity::instance()->getGravity(pos));
837     vs -= (gravity - _buoyancy - v_force_acc_fpss - normal_force_fpss) * dt;
838
839     if (vs <= 0.00001 && vs >= -0.00001)
840         vs = 0;
841
842     // set new position
843     if(_slave_load_to_ac) {
844         setOffsetPos(pos, 
845             manager->get_user_heading(),
846             manager->get_user_pitch(), 
847             manager->get_user_roll()
848             );
849         pos.setLatitudeDeg(_offsetpos.getLatitudeDeg());
850         pos.setLongitudeDeg(_offsetpos.getLongitudeDeg());
851         pos.setElevationFt(_offsetpos.getElevationFt());
852
853         if (getHtAGL(10000)){
854             double deadzone = 0.1;
855
856             if (_ht_agl_ft <= (0 + _ground_offset + deadzone) && _solid){
857                 pos.setElevationFt(0 + _ground_offset);
858             } else {
859                 pos.setElevationFt(_offsetpos.getElevationFt() + _load_offset);
860             }
861
862         }
863     } else {
864         pos.setLatitudeDeg( pos.getLatitudeDeg()
865             + (speed_north_deg_sec - wind_speed_from_north_deg_sec 
866             + force_speed_north_deg_sec + friction_force_speed_north_deg_sec) * dt );
867         pos.setLongitudeDeg( pos.getLongitudeDeg()
868             + (speed_east_deg_sec - wind_speed_from_east_deg_sec 
869             + force_speed_east_deg_sec + friction_force_speed_east_deg_sec) * dt );
870         pos.setElevationFt(pos.getElevationFt() + vs * dt);
871     }
872
873 //    cout << _name << " run hs " << hs << " vs " << vs << endl;
874
875     // recalculate total speed
876     if ( vs == 0 && hs == 0)
877         speed = 0;
878     else
879         speed = sqrt( vs * vs + hs * hs) / SG_KT_TO_FPS;
880
881     // recalculate elevation and azimuth (velocity vectors)
882     _elevation = atan2( vs, hs ) * SG_RADIANS_TO_DEGREES;
883     _azimuth =  atan2((_speed_east_fps + force_speed_east_fps + friction_force_speed_east_fps), 
884         (_speed_north_fps + force_speed_north_fps + friction_force_speed_north_fps))
885         * SG_RADIANS_TO_DEGREES;
886
887     // rationalise azimuth
888     if (_azimuth < 0)
889         _azimuth += 360;
890
891     if (_aero_stabilised) { // we simulate rotational moment of inertia by using a filter
892         //cout<< "_aero_stabilised " << hdg << " az " << _azimuth << endl;
893         const double coeff = 0.9;
894
895         // we assume a symetrical MI about the pitch and yaw axis
896         setPch(_elevation, dt, coeff);
897         setHdg(_azimuth, dt, coeff);
898     } else if (_force_stabilised) { // we simulate rotational moment of inertia by using a filter
899         //cout<< "_force_stabilised "<< endl;
900         
901         const double coeff = 0.9;
902         double ratio = h_force_lbs/(_mass * slugs_to_lbs);
903
904         if (ratio >  1) ratio =  1;
905         if (ratio < -1) ratio = -1;
906
907         double force_pitch = acos(ratio) * SG_RADIANS_TO_DEGREES;
908
909         if (force_pitch <= force_elevation_deg)
910             force_pitch = force_elevation_deg;
911
912         // we assume a symetrical MI about the pitch and yaw axis
913         setPch(force_pitch,dt, coeff);
914         setHdg(_azimuth, dt, coeff);
915     }
916
917     //do impacts and collisions
918     if (_report_impact && !_impact_reported)
919         handle_impact();
920
921     if (_report_collision && !_collision_reported)
922         handle_collision();
923
924     // set destruction flag if altitude less than sea level -1000
925     if (altitude_ft < -1000.0 && life != -1)
926         setDie(true);
927
928 }  // end Run
929
930 double FGAIBallistic::_getTime() const {
931     return _life_timer;
932 }
933
934 void FGAIBallistic::setTime(double s){
935     _life_timer = s;
936 }
937
938 void FGAIBallistic::handle_impact() {
939
940     // try terrain intersection
941     double start = pos.getElevationM() + 100;
942
943     if(!getHtAGL(start)) 
944         return;
945
946     if (_ht_agl_ft <= 0) {
947         SG_LOG(SG_AI, SG_DEBUG, "AIBallistic: terrain impact material" << _mat_name);
948         report_impact(_elevation_m);
949         _impact_reported = true;
950
951         if (life == -1){
952             invisible = true;
953         } else if (_subID == 0)  // kill the AIObject if there is no subsubmodel
954             setDie(true);
955     } 
956 }
957
958 void FGAIBallistic::handle_expiry() {
959
960     //SG_LOG(SG_AI, SG_DEBUG, "AIBallistic: handle_expiry " << pos.getElevationM());
961
962     report_impact(pos.getElevationM());
963     _expiry_reported = true;
964
965     if (life == -1){
966         invisible = true;
967     } else if (_subID == 0){  // kill the AIObject if there is no subsubmodel
968         setDie(true);
969     }
970
971 }
972
973 void FGAIBallistic::handle_collision()
974 {
975     const FGAIBase *object = manager->calcCollision(pos.getElevationFt(),
976         pos.getLatitudeDeg(),pos.getLongitudeDeg(), _fuse_range);
977
978     if (object) {
979         report_impact(pos.getElevationM(), object);
980         _collision_reported = true;
981     }
982 }
983
984 void FGAIBallistic::report_impact(double elevation, const FGAIBase *object)
985 {
986     _impact_lat    = pos.getLatitudeDeg();
987     _impact_lon    = pos.getLongitudeDeg();
988     _impact_elev   = elevation;
989     _impact_speed  = speed * SG_KT_TO_MPS;
990     _impact_hdg    = hdg;
991     _impact_pitch  = pitch;
992     _impact_roll   = roll;
993
994     SGPropertyNode *n = props->getNode("impact", true);
995
996     if (object)
997         n->setStringValue("type", object->getTypeString());
998     else
999         n->setStringValue("type", "terrain");
1000
1001     SG_LOG(SG_AI, SG_DEBUG, "AIBallistic: object impact " << _name 
1002         << " lon " <<_impact_lon << " lat " <<_impact_lat << " sec " << _life_timer);
1003
1004     n->setDoubleValue("longitude-deg", _impact_lon);
1005     n->setDoubleValue("latitude-deg", _impact_lat);
1006     n->setDoubleValue("elevation-m", _impact_elev);
1007     n->setDoubleValue("heading-deg", _impact_hdg);
1008     n->setDoubleValue("pitch-deg", _impact_pitch);
1009     n->setDoubleValue("roll-deg", _impact_roll);
1010     n->setDoubleValue("speed-mps", _impact_speed);
1011
1012     _impact_report_node->setStringValue(props->getPath());
1013 }
1014
1015 SGVec3d FGAIBallistic::getCartHitchPos() const{
1016
1017     // convert geodetic positions to geocentered
1018     SGVec3d cartuserPos = globals->get_aircraft_position_cart();
1019     
1020     //SGVec3d cartPos = getCartPos();
1021
1022     // Transform to the right coordinate frame, configuration is done in
1023     // the x-forward, y-right, z-up coordinates (feet), computation
1024     // in the simulation usual body x-forward, y-right, z-down coordinates
1025     // (meters) )
1026     SGVec3d _off(_x_offset * SG_FEET_TO_METER,
1027             _y_offset * SG_FEET_TO_METER,
1028             -_z_offset * SG_FEET_TO_METER);
1029
1030     // Transform the user position to the horizontal local coordinate system.
1031     SGQuatd hlTrans = SGQuatd::fromLonLat(globals->get_aircraft_position());
1032
1033     // and postrotate the orientation of the user model wrt the horizontal
1034     // local frame
1035     hlTrans *= SGQuatd::fromYawPitchRollDeg(
1036         manager->get_user_heading(),
1037         manager->get_user_pitch(),
1038         manager->get_user_roll());
1039
1040     // The offset converted to the usual body fixed coordinate system
1041     // rotated to the earth-fixed coordinates axis
1042     SGVec3d off = hlTrans.backTransform(_off);
1043
1044     // Add the position offset of the user model to get the geocentered position
1045     SGVec3d offsetPos = cartuserPos + off;
1046
1047     return offsetPos;
1048 }
1049
1050 void FGAIBallistic::setOffsetPos(SGGeod inpos, double heading, double pitch, double roll){
1051     // convert the hitch geocentered position to geodetic
1052
1053     SGVec3d cartoffsetPos = getCartOffsetPos(inpos, heading, pitch, roll);
1054
1055     //SGVec3d cartoffsetPos = getCartHitchPos();
1056
1057     //SGGeodesy::SGCartToGeod(cartoffsetPos, hitchpos);
1058     SGGeodesy::SGCartToGeod(cartoffsetPos, _offsetpos);
1059
1060 }
1061
1062 double FGAIBallistic::getDistanceToHitch() const {
1063     //calculate the distance load to hitch 
1064     SGVec3d carthitchPos = getCartHitchPos();
1065     SGVec3d cartPos = getCartPos();
1066
1067     SGVec3d diff = carthitchPos - cartPos;
1068     double distance = norm(diff);
1069     return distance * SG_METER_TO_FEET;
1070 }
1071
1072 double FGAIBallistic::getElevToHitch() const {
1073     // now the angle, positive angles are upwards
1074     double distance = getDistanceToHitch() * SG_FEET_TO_METER;
1075     double angle = 0;
1076     double daltM = _offsetpos.getElevationM() - pos.getElevationM();
1077
1078     if (fabs(distance) < SGLimits<float>::min()) {
1079         angle = 0;
1080     } else {
1081         double sAngle = daltM/distance;
1082         sAngle = SGMiscd::min(1, SGMiscd::max(-1, sAngle));
1083         angle = SGMiscd::rad2deg(asin(sAngle));
1084     }
1085
1086     return angle;
1087 }
1088
1089 double FGAIBallistic::getBearingToHitch() const {
1090     //calculate the bearing and range of the second pos from the first
1091     double distance = getDistanceToHitch() * SG_FEET_TO_METER;
1092     double az1, az2;
1093
1094     geo_inverse_wgs_84(pos, _offsetpos, &az1, &az2, &distance);
1095
1096     return az1;
1097 }
1098
1099 double FGAIBallistic::getRelBrgHitchToUser() const {
1100     //calculate the relative bearing 
1101     double az1, az2, distance;
1102
1103     geo_inverse_wgs_84(_offsetpos, globals->get_aircraft_position(), &az1, &az2, &distance);
1104
1105     double rel_brg = az1 - hdg;
1106
1107     SG_NORMALIZE_RANGE(rel_brg, -180.0, 180.0);
1108
1109     return rel_brg;
1110 }
1111
1112 double FGAIBallistic::getElevHitchToUser() const {
1113
1114     //calculate the distance from the user position
1115     SGVec3d carthitchPos = getCartHitchPos();
1116     SGVec3d cartuserPos = globals->get_aircraft_position_cart();
1117
1118     SGVec3d diff = cartuserPos - carthitchPos;
1119
1120     double distance = norm(diff);
1121     double angle = 0;
1122
1123     double daltM = globals->get_aircraft_position().getElevationM() - _offsetpos.getElevationM();
1124
1125     // now the angle, positive angles are upwards
1126     if (fabs(distance) < SGLimits<float>::min()) {
1127         angle = 0;
1128     } else {
1129         double sAngle = daltM/distance;
1130         sAngle = SGMiscd::min(1, SGMiscd::max(-1, sAngle));
1131         angle = SGMiscd::rad2deg(asin(sAngle));
1132     }
1133
1134     return angle;
1135 }
1136
1137 void FGAIBallistic::setTgtOffsets(double dt, double coeff){
1138     double c = dt / (coeff + dt);
1139
1140     _x_offset = (_tgt_x_offset * c) + (_x_offset * (1 - c));
1141     _y_offset = (_tgt_y_offset * c) + (_y_offset * (1 - c));
1142     _z_offset = (_tgt_z_offset * c) + (_z_offset * (1 - c));
1143 }
1144
1145
1146 void FGAIBallistic::calcVSHS(){
1147     // calculate vertical and horizontal speed components
1148     double speed_fps = speed * SG_KT_TO_FPS;
1149
1150     if (speed == 0.0) {
1151         hs = vs = 0.0;
1152     } else {
1153         vs = sin( _elevation * SG_DEGREES_TO_RADIANS ) * speed_fps;
1154         hs = cos( _elevation * SG_DEGREES_TO_RADIANS ) * speed_fps;
1155     }
1156 }
1157
1158 void FGAIBallistic::calcNE(){
1159     //resolve horizontal speed into north and east components:
1160     _speed_north_fps = cos(_azimuth / SG_RADIANS_TO_DEGREES) * hs;
1161     _speed_east_fps = sin(_azimuth / SG_RADIANS_TO_DEGREES) * hs;
1162
1163     // convert horizontal speed (fps) to degrees per second
1164     speed_north_deg_sec = _speed_north_fps / ft_per_deg_lat;
1165     speed_east_deg_sec  = _speed_east_fps / ft_per_deg_lon;
1166
1167 }
1168
1169 SGVec3d FGAIBallistic::getCartOffsetPos(SGGeod inpos, double user_heading, 
1170                                         double user_pitch, double user_roll
1171                                         ) const{
1172
1173     // convert geodetic positions to geocentered
1174      SGVec3d cartuserPos = SGVec3d::fromGeod(inpos);
1175     //SGVec3d cartuserPos = getCartUserPos();
1176     //SGVec3d cartPos = getCartPos();
1177
1178     // Transform to the right coordinate frame, configuration is done in
1179     // the x-forward, y-right, z-up coordinates (feet), computation
1180     // in the simulation usual body x-forward, y-right, z-down coordinates
1181     // (meters) )
1182     SGVec3d _off(_x_offset * SG_FEET_TO_METER,
1183             _y_offset * SG_FEET_TO_METER,
1184             -_z_offset * SG_FEET_TO_METER);
1185
1186     // Transform the user position to the horizontal local coordinate system.
1187     SGQuatd hlTrans = SGQuatd::fromLonLat(inpos);
1188
1189     // and postrotate the orientation of the user model wrt the horizontal
1190     // local frame
1191     hlTrans *= SGQuatd::fromYawPitchRollDeg(
1192         user_heading,
1193         user_pitch,
1194         user_roll);
1195
1196     // The offset converted to the usual body fixed coordinate system
1197     // rotated to the earth-fixed coordinates axis
1198     SGVec3d off = hlTrans.backTransform(_off);
1199
1200     // Add the position offset of the user model to get the geocentered position
1201     SGVec3d offsetPos = cartuserPos + off;
1202
1203     return offsetPos;
1204 }
1205
1206 void FGAIBallistic::setOffsetVelocity(double dt, SGGeod offsetpos) {
1207     //calculate the distance from the previous offset position
1208     SGVec3d cartoffsetPos = SGVec3d::fromGeod(offsetpos);
1209     SGVec3d diff = cartoffsetPos - _oldcartoffsetPos;
1210
1211     double distance = norm(diff);
1212     //calculate speed knots
1213     speed = (distance/dt) * SG_MPS_TO_KT;
1214
1215     //now calulate the angle between the old and current postion positions (degrees)
1216     double angle = 0;
1217     double daltM = offsetpos.getElevationM() - _oldoffsetpos.getElevationM();
1218
1219     if (fabs(distance) < SGLimits<float>::min()) {
1220         angle = 0;
1221     } else {
1222         double sAngle = daltM/distance;
1223         sAngle = SGMiscd::min(1, SGMiscd::max(-1, sAngle));
1224         angle = SGMiscd::rad2deg(asin(sAngle));
1225     }
1226
1227     _elevation = angle;
1228
1229     //calculate vertical and horizontal speed components
1230     calcVSHS();
1231
1232     //calculate the bearing of the new offset position from the old
1233     //don't do this if speed is low
1234     //cout << "speed " << speed << endl;
1235     if (speed > 0.1){
1236         double az1, az2, dist;
1237         geo_inverse_wgs_84(_oldoffsetpos, offsetpos, &az1, &az2, &dist);
1238         _azimuth = az1;
1239         //cout << "offset az " << _azimuth << endl;
1240     } else {
1241         _azimuth = hdg;
1242         //cout << " slow offset az " << _azimuth << endl;
1243     }
1244
1245     //resolve horizontal speed into north and east components:
1246     calcNE();
1247
1248     // and finally store the new values
1249     _oldcartoffsetPos = cartoffsetPos;
1250     _oldoffsetpos = offsetpos;
1251 }
1252
1253 // end AIBallistic