]> git.mxchange.org Git - flightgear.git/blob - src/AIModel/AIEscort.cxx
80ef3232579af69fa89973d241c883a3f21aab7a
[flightgear.git] / src / AIModel / AIEscort.cxx
1 // FGAIEscort - FGAIShip-derived class creates an AI Ground Vehicle
2 // by adding a ground following utility
3 //
4 // Written by Vivian Meazza, started August 2009.
5 // - vivian.meazza at lineone.net
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 #ifdef HAVE_CONFIG_H
22 #  include <config.h>
23 #endif
24
25 #include <algorithm>
26 #include <string>
27 #include <vector>
28
29 #include <simgear/sg_inlines.h>
30 #include <simgear/math/SGMath.hxx>
31 #include <simgear/math/sg_geodesy.hxx>
32
33 #include <math.h>
34 #include <Main/util.hxx>
35 #include <Main/viewer.hxx>
36
37 #include <Scenery/scenery.hxx>
38 #include <Scenery/tilemgr.hxx>
39
40 #include "AIEscort.hxx"
41
42 FGAIEscort::FGAIEscort() :
43 FGAIShip(otEscort),
44
45 _selected_ac(0),
46 _relbrg (0),
47 _stn_truebrg(0),
48 _parent_speed(0),
49 _stn_limit(0),
50 _stn_angle_limit(0),
51 _stn_speed(0),
52 _stn_height(0),
53 _max_speed(0),
54 _interval(0),
55 _MPControl(false),
56 _patrol(false),
57 _stn_deg_true(false),
58 _parent("")
59
60 {
61     invisible = false;
62 }
63
64 FGAIEscort::~FGAIEscort() {}
65
66 void FGAIEscort::readFromScenario(SGPropertyNode* scFileNode) {
67     if (!scFileNode)
68         return;
69
70     FGAIShip::readFromScenario(scFileNode);
71
72     setName(scFileNode->getStringValue("name", "Escort"));
73     setSMPath(scFileNode->getStringValue("submodel-path", ""));
74     setStnRange(scFileNode->getDoubleValue("station/range-nm", 1));
75     setStnBrg(scFileNode->getDoubleValue("station/brg-deg", 0.0));
76     setStnLimit(scFileNode->getDoubleValue("station/range-limit-nm", 0.2));
77     setStnAngleLimit(scFileNode->getDoubleValue("station/angle-limit-deg", 15.0));
78     setStnSpeed(scFileNode->getDoubleValue("station/speed-kts", 2.5));
79     setStnPatrol(scFileNode->getBoolValue("station/patrol", false));
80     setStnHtFt(scFileNode->getDoubleValue("station/height-ft", 0.0));
81     setStnDegTrue(scFileNode->getBoolValue("station/deg-true", false));
82     setParentName(scFileNode->getStringValue("station/parent", ""));
83     setMaxSpeed(scFileNode->getDoubleValue("max-speed-kts", 30.0));
84     setUpdateInterval(scFileNode->getDoubleValue("update-interval-sec", 10.0));
85     setCallSign(scFileNode->getStringValue("callsign", ""));
86
87     if(_patrol)
88         sg_srandom_time();
89
90 }
91
92 void FGAIEscort::bind() {
93     FGAIShip::bind();
94
95     props->tie("station/rel-bearing-deg",
96         SGRawValuePointer<double>(&_stn_relbrg));
97     props->tie("station/true-bearing-deg",
98         SGRawValuePointer<double>(&_stn_truebrg));
99     props->tie("station/range-nm",
100         SGRawValuePointer<double>(&_stn_range));
101     props->tie("station/range-limit-nm",
102         SGRawValuePointer<double>(&_stn_limit));
103     props->tie("station/angle-limit-deg",
104         SGRawValuePointer<double>(&_stn_angle_limit));
105     props->tie("station/speed-kts",
106         SGRawValuePointer<double>(&_stn_speed));
107     props->tie("station/height-ft",
108         SGRawValuePointer<double>(&_stn_height));
109     props->tie("controls/update-interval-sec",
110         SGRawValuePointer<double>(&_interval));
111     props->tie("controls/parent-mp-control",
112         SGRawValuePointer<bool>(&_MPControl));
113     props->tie("station/target-range-nm",
114         SGRawValuePointer<double>(&_tgtrange));
115     props->tie("station/target-brg-deg-t",
116         SGRawValuePointer<double>(&_tgtbrg));
117     props->tie("station/patrol",
118         SGRawValuePointer<bool>(&_patrol));
119 }
120
121 void FGAIEscort::unbind() {
122     FGAIShip::unbind();
123
124     props->untie("station/rel-bearing-deg");
125     props->untie("station/true-bearing-deg");
126     props->untie("station/range-nm");
127     props->untie("station/range-limit-nm");
128     props->untie("station/angle-limit-deg");
129     props->untie("station/speed-kts");
130     props->untie("station/height-ft");
131     props->untie("controls/update-interval-sec");
132
133 }
134
135 bool FGAIEscort::init(bool search_in_AI_path) {
136     if (!FGAIShip::init(search_in_AI_path))
137         return false;
138
139     invisible = false;
140     no_roll = false;
141
142     props->setStringValue("controls/parent-name", _parent.c_str());
143     setParent();
144     pos = _tgtpos;
145     speed = _parent_speed;
146     hdg = _parent_hdg;
147
148     return true;
149 }
150
151 void FGAIEscort::update(double dt) {
152     FGAIShip::update(dt);
153
154     RunEscort(dt);
155 }
156
157 void FGAIEscort::setStnRange(double r) {
158     _stn_range = r;
159 }
160
161 void FGAIEscort::setStnBrg(double b) {
162     _stn_brg = b;
163 }
164
165 void FGAIEscort::setStnLimit(double l) {
166     _stn_limit = l;
167 }
168
169 void FGAIEscort::setStnAngleLimit(double al) {
170     _stn_angle_limit = al;
171 }
172
173 void FGAIEscort::setStnSpeed(double s) {
174     _stn_speed = s;
175 }
176
177 void FGAIEscort::setStnHtFt(double h) {
178     _stn_height = h;
179 }
180
181 void FGAIEscort::setStnDegTrue(bool t) {
182     _stn_deg_true = t;
183 }
184
185 void FGAIEscort::setMaxSpeed(double m) {
186     _max_speed = m;
187 }
188
189 void FGAIEscort::setUpdateInterval(double i) {
190     _interval = i;
191 }
192
193 void FGAIEscort::setStnPatrol(bool p) {
194     _patrol = p;
195 }
196
197 void FGAIEscort::setParentName(const string& p) {
198     _parent = p;
199 }
200
201 bool FGAIEscort::getGroundElev(SGGeod inpos) {
202
203     double height_m ;
204
205     if (globals->get_scenery()->get_elevation_m(SGGeod::fromGeodM(inpos, 3000), height_m, &_material,0)){
206         _ht_agl_ft = inpos.getElevationFt() - height_m * SG_METER_TO_FEET;
207
208         if (_material) {
209             const vector<string>& names = _material->get_names();
210
211             _solid = _material->get_solid();
212
213             if (!names.empty())
214                 props->setStringValue("material/name", names[0].c_str());
215             else
216                 props->setStringValue("material/name", "");
217
218             //cout << "material " << names[0].c_str()
219             //    << " _elevation_m " << _elevation_m
220             //    << " solid " << _solid
221             //    << " load " << _load_resistance
222             //    << " frictionFactor " << _frictionFactor
223             //    << endl;
224
225         }
226
227         return true;
228     } else {
229         return false;
230     }
231
232 }
233
234 void FGAIEscort::setParent() {
235
236     const SGPropertyNode *ai = fgGetNode("/ai/models", true);
237
238     for (int i = ai->nChildren() - 1; i >= -1; i--) {
239         const SGPropertyNode *model;
240
241         if (i < 0) { // last iteration: selected model
242             model = _selected_ac;
243         } else {
244             model = ai->getChild(i);
245             string path = ai->getPath();
246             const string name = model->getStringValue("name");
247
248             if (!model->nChildren()){
249                 continue;
250             }
251             if (name == _parent) {
252                 _selected_ac = model;  // save selected model for last iteration
253                 break;
254             }
255
256         }
257         if (!model)
258             continue;
259
260     }// end for loop
261
262     if (_selected_ac != 0){
263         const string name = _selected_ac->getStringValue("name");
264         double lat = _selected_ac->getDoubleValue("position/latitude-deg");
265         double lon = _selected_ac->getDoubleValue("position/longitude-deg");
266         double elevation = _selected_ac->getDoubleValue("position/altitude-ft");
267         _MPControl = _selected_ac->getBoolValue("controls/mp-control");
268
269         _selectedpos.setLatitudeDeg(lat);
270         _selectedpos.setLongitudeDeg(lon);
271         _selectedpos.setElevationFt(elevation);
272
273         _parent_speed    = _selected_ac->getDoubleValue("velocities/speed-kts");
274         _parent_hdg      = _selected_ac->getDoubleValue("orientation/true-heading-deg");
275
276         if(!_stn_deg_true){
277             _stn_truebrg = calcTrueBearingDeg(_stn_brg, _parent_hdg);
278             _stn_relbrg = _stn_brg;
279             //cout << _name <<" set rel"<<endl;
280         } else {
281             _stn_truebrg = _stn_brg;
282             _stn_relbrg = calcRelBearingDeg(_stn_brg, _parent_hdg); 
283             //cout << _name << " set true"<<endl;
284         }
285
286         double course2;
287
288         SGGeodesy::direct( _selectedpos, _stn_truebrg, _stn_range * SG_NM_TO_METER,
289             _tgtpos, course2);
290
291         _tgtpos.setElevationFt(_stn_height);
292
293         calcRangeBearing(pos.getLatitudeDeg(), pos.getLongitudeDeg(),
294             _tgtpos.getLatitudeDeg(), _tgtpos.getLongitudeDeg(), _tgtrange, _tgtbrg);
295
296         _relbrg = calcRelBearingDeg(_tgtbrg, hdg);
297
298     } else {
299         SG_LOG(SG_GENERAL, SG_ALERT, "AIEscort: " << _name
300             << " parent not found: dying ");
301         setDie(true);
302     }
303
304 }
305
306 void FGAIEscort::calcRangeBearing(double lat, double lon, double lat2, double lon2,
307                                   double &range, double &bearing) const
308 {
309     // calculate the bearing and range of the second pos from the first
310     double az2, distance;
311     geo_inverse_wgs_84(lat, lon, lat2, lon2, &bearing, &az2, &distance);
312     range = distance * SG_METER_TO_NM;
313 }
314
315 double FGAIEscort::calcRelBearingDeg(double bearing, double heading)
316 {
317     double angle = bearing - heading;
318     SG_NORMALIZE_RANGE(angle, -180.0, 180.0);
319     return angle;
320 }
321
322 double FGAIEscort::calcTrueBearingDeg(double bearing, double heading)
323 {
324     double angle = bearing + heading;
325     SG_NORMALIZE_RANGE(angle, 0.0, 360.0);
326     return angle;
327 }
328
329 double FGAIEscort::calcRecipBearingDeg(double bearing)
330 {
331     double angle = bearing - 180;
332     SG_NORMALIZE_RANGE(angle, 0.0, 360.0);
333     return angle;
334 }
335
336 SGVec3d FGAIEscort::getCartHitchPosAt(const SGVec3d& _off) const {
337     double hdg = _selected_ac->getDoubleValue("orientation/true-heading-deg");
338     double pitch = _selected_ac->getDoubleValue("orientation/pitch-deg");
339     double roll = _selected_ac->getDoubleValue("orientation/roll-deg");
340
341     // Transform that one to the horizontal local coordinate system.
342     SGQuatd hlTrans = SGQuatd::fromLonLat(_selectedpos);
343
344     // and postrotate the orientation of the AIModel wrt the horizontal
345     // local frame
346     hlTrans *= SGQuatd::fromYawPitchRollDeg(hdg, pitch, roll);
347
348     // The offset converted to the usual body fixed coordinate system
349     // rotated to the earth fiexed coordinates axis
350     SGVec3d off = hlTrans.backTransform(_off);
351
352     // Add the position offset of the AIModel to gain the earth centered position
353     SGVec3d cartPos = SGVec3d::fromGeod(_selectedpos);
354
355     return cartPos + off;
356 }
357
358
359 void FGAIEscort::setStationSpeed(){
360
361     double speed = 0;
362     double angle = 0;
363
364     // these are the AI rules for the manoeuvring of escorts
365
366     if (_MPControl && _tgtrange > 4 * _stn_limit){
367         SG_LOG(SG_GENERAL, SG_ALERT, "AIEscort: " << _name
368             << " re-aligning to MP pos");
369         pos = _tgtpos;
370         speed = 0;
371         angle = 0;
372     }else if ((_relbrg < -90 || _relbrg > 90) && _tgtrange > _stn_limit ){
373         angle =_relbrg;
374
375         if(_tgtrange > 4 * _stn_limit)
376             speed = 4 * -_stn_speed;
377         else
378             speed = -_stn_speed;
379
380     }else if ((_relbrg >= -90 || _relbrg <= 90) && _tgtrange > _stn_limit){
381         angle = _relbrg;
382
383         if(_tgtrange > 4 * _stn_limit)
384             speed = 4 * _stn_speed;
385         else
386             speed = _stn_speed;
387
388     } else {
389
390         if(_patrol){
391             angle = 15 * sg_random();
392             speed =  5 * sg_random();
393         } else {
394             angle = 0;
395             speed = 0;
396         }
397
398     }
399
400     double station_speed = _parent_speed + speed;
401
402     SG_CLAMP_RANGE(station_speed, 5.0, _max_speed);
403     SG_CLAMP_RANGE(angle, -_stn_angle_limit, _stn_angle_limit);
404
405     AccelTo(station_speed);
406     TurnTo(_parent_hdg + angle);
407     ClimbTo(_stn_height);
408
409 }
410
411 void FGAIEscort::RunEscort(double dt){
412
413     _dt_count += dt;
414
415
416
417     ///////////////////////////////////////////////////////////////////////////
418     // Check execution time (currently once every 0.05 sec or 20 fps)
419     // Add a bit of randomization to prevent the execution of all flight plans
420     // in synchrony, which can add significant periodic framerate flutter.
421     // Randomization removed to get better appearance
422     ///////////////////////////////////////////////////////////////////////////
423
424     //cout << "_start_sec " << _start_sec << " time_sec " << time_sec << endl;
425     if (_dt_count < _next_run)
426         return;
427     _next_run = _interval /*+ (0.015 * sg_random())*/;
428
429     if(_parent == ""){
430         return;
431     }
432
433     setParent();
434     setStationSpeed();
435     //getGroundElev(pos);
436
437     _dt_count = 0;
438
439 }
440
441 // end AIGroundvehicle