]> git.mxchange.org Git - flightgear.git/blob - src/Airports/dynamics.cxx
fix array index out of bounds in dynamics.cxx
[flightgear.git] / src / Airports / dynamics.cxx
1 // dynamics.cxx - Code to manage the higher order airport ground activities
2 // Written by Durk Talsma, started December 2004.
3 //
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 //
19 // $Id$
20
21 #ifdef HAVE_CONFIG_H
22 #  include <config.h>
23 #endif
24
25 #include <algorithm>
26
27 #include <simgear/compiler.h>
28
29 #include <Environment/environment_mgr.hxx>
30 #include <Environment/environment.hxx>
31 #include <simgear/misc/sg_path.hxx>
32 #include <simgear/props/props.hxx>
33 #include <simgear/structure/subsystem_mgr.hxx>
34 #include <simgear/debug/logstream.hxx>
35 #include <simgear/route/waypoint.hxx>
36 #include <Main/globals.hxx>
37 #include <Main/fg_props.hxx>
38 #include <Airports/runways.hxx>
39 #include <ATCDCL/ATCutils.hxx>
40
41 #include <string>
42 #include <vector>
43
44 using std::string;
45 using std::vector;
46 using std::sort;
47 using std::random_shuffle;
48
49 #include "simple.hxx"
50 #include "dynamics.hxx"
51
52 FGAirportDynamics::FGAirportDynamics(FGAirport * ap):
53     _ap(ap), rwyPrefs(ap), SIDs(ap),
54         startupController    (this),
55     towerController      (this),
56     approachController   (this),
57     atisSequenceIndex(-1),
58     atisSequenceTimeStamp(0.0)
59
60 {
61     lastUpdate = 0;
62 }
63
64 // Destructor
65 FGAirportDynamics::~FGAirportDynamics()
66 {
67 }
68
69
70 // Initialization required after XMLRead
71 void FGAirportDynamics::init()
72 {
73     // This may seem a bit weird to first randomly shuffle the parkings
74     // and then sort them again. However, parkings are sorted here by ascending 
75     // radius. Since many parkings have similar radii, with each radius class they will
76     // still be allocated relatively systematically. Randomizing prior to sorting will
77     // prevent any initial orderings to be destroyed, leading (hopefully) to a more 
78     // naturalistic gate assignment. 
79     random_shuffle(parkings.begin(), parkings.end());
80     sort(parkings.begin(), parkings.end());
81     // add the gate positions to the ground network. 
82     groundNetwork.setParent(_ap);
83     groundNetwork.addNodes(&parkings);
84     groundNetwork.init();
85     groundNetwork.setTowerController(&towerController);
86     
87 }
88
89 bool FGAirportDynamics::getAvailableParking(double *lat, double *lon,
90                                             double *heading, int *gateId,
91                                             double rad,
92                                             const string & flType,
93                                             const string & acType,
94                                             const string & airline)
95 {
96     bool found = false;
97     bool available = false;
98
99
100     FGParkingVecIterator i;
101     if (parkings.begin() == parkings.end()) {
102         //cerr << "Could not find parking spot at " << _ap->getId() << endl;
103         *lat = _ap->getLatitude();
104         *lon = _ap->getLongitude();
105         *heading = 0;
106         found = true;
107     } else {
108         // First try finding a parking with a designated airline code
109         for (i = parkings.begin(); !(i == parkings.end() || found); i++) {
110             available = true;
111             // Taken by another aircraft
112             if (!(i->isAvailable())) {
113                 available = false;
114                 continue;
115             }
116             // No airline codes, so skip
117             if (i->getCodes().empty()) {
118                 available = false;
119                 continue;
120             } else {             // Airline code doesn't match
121                 //cerr << "Code = " << airline << ": Codes " << i->getCodes();
122                 if (i->getCodes().find(airline, 0) == string::npos) {
123                     available = false;
124                     //cerr << "Unavailable" << endl;
125                     continue;
126                 } else {
127                     //cerr << "Available" << endl;
128                 }
129             }
130             // Type doesn't match
131             if (i->getType() != flType) {
132                 available = false;
133                 continue;
134             }
135             // too small
136             if (i->getRadius() < rad) {
137                 available = false;
138                 continue;
139             }
140
141             if (available) {
142                 *lat = i->getLatitude();
143                 *lon = i->getLongitude();
144                 *heading = i->getHeading();
145                 *gateId = i->getIndex();
146                 i->setAvailable(false);
147                 found = true;
148             }
149         }
150         // then try again for those without codes. 
151         for (i = parkings.begin(); !(i == parkings.end() || found); i++) {
152             available = true;
153             if (!(i->isAvailable())) {
154                 available = false;
155                 continue;
156             }
157             if (!(i->getCodes().empty())) {
158                 if ((i->getCodes().find(airline, 0) == string::npos)) {
159                     available = false;
160                     continue;
161                 }
162             }
163             if (i->getType() != flType) {
164                 available = false;
165                 continue;
166             }
167
168             if (i->getRadius() < rad) {
169                 available = false;
170                 continue;
171             }
172
173             if (available) {
174                 *lat = i->getLatitude();
175                 *lon = i->getLongitude();
176                 *heading = i->getHeading();
177                 *gateId = i->getIndex();
178                 i->setAvailable(false);
179                 found = true;
180             }
181         }
182         // And finally once more if that didn't work. Now ignore the airline codes, as a last resort
183         for (i = parkings.begin(); !(i == parkings.end() || found); i++) {
184             available = true;
185             if (!(i->isAvailable())) {
186                 available = false;
187                 continue;
188             }
189             if (i->getType() != flType) {
190                 available = false;
191                 continue;
192             }
193
194             if (i->getRadius() < rad) {
195                 available = false;
196                 continue;
197             }
198
199             if (available) {
200                 *lat = i->getLatitude();
201                 *lon = i->getLongitude();
202                 *heading = i->getHeading();
203                 *gateId = i->getIndex();
204                 i->setAvailable(false);
205                 found = true;
206             }
207         }
208     }
209     if (!found) {
210         //cerr << "Traffic overflow at" << _ap->getId() 
211         //           << ". flType = " << flType 
212         //           << ". airline = " << airline 
213         //           << " Radius = " <<rad
214         //           << endl;
215         *lat = _ap->getLatitude();
216         *lon = _ap->getLongitude();
217         *heading = 0;
218         *gateId = -1;
219         //exit(1);
220     }
221     return found;
222 }
223
224 void FGAirportDynamics::getParking(int id, double *lat, double *lon,
225                                    double *heading)
226 {
227     if (id < 0) {
228         *lat = _ap->getLatitude();
229         *lon = _ap->getLongitude();
230         *heading = 0;
231     } else {
232         FGParkingVecIterator i = parkings.begin();
233         for (i = parkings.begin(); i != parkings.end(); i++) {
234             if (id == i->getIndex()) {
235                 *lat = i->getLatitude();
236                 *lon = i->getLongitude();
237                 *heading = i->getHeading();
238             }
239         }
240     }
241 }
242
243 FGParking *FGAirportDynamics::getParking(int id)
244 {
245     FGParkingVecIterator i = parkings.begin();
246     for (i = parkings.begin(); i != parkings.end(); i++) {
247         if (id == i->getIndex()) {
248             return &(*i);
249         }
250     }
251     return 0;
252 }
253
254 string FGAirportDynamics::getParkingName(int id)
255 {
256     FGParkingVecIterator i = parkings.begin();
257     for (i = parkings.begin(); i != parkings.end(); i++) {
258         if (id == i->getIndex()) {
259             return i->getName();
260         }
261     }
262
263     return string("overflow");
264 }
265
266 void FGAirportDynamics::releaseParking(int id)
267 {
268     if (id >= 0) {
269
270         FGParkingVecIterator i = parkings.begin();
271         for (i = parkings.begin(); i != parkings.end(); i++) {
272             if (id == i->getIndex()) {
273                 i->setAvailable(true);
274             }
275         }
276     }
277 }
278
279 void FGAirportDynamics::setRwyUse(const FGRunwayPreference & ref)
280 {
281     rwyPrefs = ref;
282     //cerr << "Exiting due to not implemented yet" << endl;
283     //exit(1);
284 }
285
286 bool FGAirportDynamics::innerGetActiveRunway(const string & trafficType,
287                                              int action, string & runway,
288                                              double heading)
289 {
290     double windSpeed;
291     double windHeading;
292     double maxTail;
293     double maxCross;
294     string name;
295     string type;
296
297     if (!rwyPrefs.available()) {
298         return false;
299     }
300
301     RunwayGroup *currRunwayGroup = 0;
302     int nrActiveRunways = 0;
303     time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
304     if ((abs((long) (dayStart - lastUpdate)) > 600)
305         || trafficType != prevTrafficType) {
306         landing.clear();
307         takeoff.clear();
308         lastUpdate = dayStart;
309         prevTrafficType = trafficType;
310         /*
311         FGEnvironment
312             stationweather =
313             ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
314             ->getEnvironment(getLatitude(), getLongitude(),
315                              getElevation());
316         */
317         windSpeed   = fgGetInt("/environment/metar/base-wind-speed-kt"); //stationweather.get_wind_speed_kt();
318         windHeading = fgGetInt("/environment/metar/base-wind-dir-deg");
319         //stationweather.get_wind_from_heading_deg();
320         string scheduleName;
321         //cerr << "finding active Runway for : " << _ap->getId() << endl;
322         //cerr << "Wind Heading              : " << windHeading << endl;
323         //cerr << "Wind Speed                : " << windSpeed << endl;
324
325         //cerr << "Nr of seconds since day start << " << dayStart << endl;
326
327         ScheduleTime *currSched;
328         //cerr << "A"<< endl;
329         currSched = rwyPrefs.getSchedule(trafficType.c_str());
330         if (!(currSched))
331             return false;
332         //cerr << "B"<< endl;
333         scheduleName = currSched->getName(dayStart);
334         maxTail = currSched->getTailWind();
335         maxCross = currSched->getCrossWind();
336         //cerr << "Current Schedule =        : " << scheduleName << endl;
337         if (scheduleName.empty())
338             return false;
339         //cerr << "C"<< endl;
340         currRunwayGroup = rwyPrefs.getGroup(scheduleName);
341         //cerr << "D"<< endl;
342         if (!(currRunwayGroup))
343             return false;
344         nrActiveRunways = currRunwayGroup->getNrActiveRunways();
345
346         // Keep a history of the currently active runways, to ensure
347         // that an already established selection of runways will not
348         // be overridden once a more preferred selection becomes 
349         // available as that can lead to random runway swapping.
350         if (trafficType == "com") {
351             currentlyActive = &comActive;
352         } else if (trafficType == "gen") {
353             currentlyActive = &genActive;
354         } else if (trafficType == "mil") {
355             currentlyActive = &milActive;
356         } else if (trafficType == "ul") {
357             currentlyActive = &ulActive;
358         }
359
360         //cerr << "Durrently active selection for " << trafficType << ": ";
361         for (stringVecIterator it = currentlyActive->begin();
362              it != currentlyActive->end(); it++) {
363              //cerr << (*it) << " ";
364          }
365          //cerr << endl;
366
367         currRunwayGroup->setActive(_ap,
368                                    windSpeed,
369                                    windHeading,
370                                    maxTail, maxCross, currentlyActive);
371
372         // Note that I SHOULD keep multiple lists in memory, one for 
373         // general aviation, one for commercial and one for military
374         // traffic.
375         currentlyActive->clear();
376         nrActiveRunways = currRunwayGroup->getNrActiveRunways();
377         //cerr << "Choosing runway for " << trafficType << endl;
378         for (int i = 0; i < nrActiveRunways; i++) {
379             type = "unknown";   // initialize to something other than landing or takeoff
380             currRunwayGroup->getActive(i, name, type);
381             if (type == "landing") {
382                 landing.push_back(name);
383                 currentlyActive->push_back(name);
384                 //cerr << "Landing " << name << endl; 
385             }
386             if (type == "takeoff") {
387                 takeoff.push_back(name);
388                 currentlyActive->push_back(name);
389                 //cerr << "takeoff " << name << endl;
390             }
391         }
392         //cerr << endl;
393     }
394
395     if (action == 1)            // takeoff 
396     {
397         int nr = takeoff.size();
398         if (nr) {
399             // Note that the randomization below, is just a placeholder to choose between
400             // multiple active runways for this action. This should be
401             // under ATC control.
402             runway = chooseRwyByHeading(takeoff, heading);
403         } else {                // Fallback
404             runway = chooseRunwayFallback();
405         }
406     }
407
408     if (action == 2)            // landing
409     {
410         int nr = landing.size();
411         if (nr) {
412             runway = chooseRwyByHeading(landing, heading);
413         } else {                //fallback
414             runway = chooseRunwayFallback();
415         }
416     }
417
418     return true;
419 }
420
421 string FGAirportDynamics::chooseRwyByHeading(stringVec rwys,
422                                              double heading)
423 {
424     double bestError = 360.0;
425     double rwyHeading, headingError;
426     string runway;
427     for (stringVecIterator i = rwys.begin(); i != rwys.end(); i++) {
428         if (!_ap->hasRunwayWithIdent(*i)) {
429           SG_LOG(SG_ATC, SG_WARN, "chooseRwyByHeading: runway " << *i <<
430             " not found at " << _ap->ident());
431           continue;
432         }
433         
434         FGRunway *rwy = _ap->getRunwayByIdent((*i));
435         rwyHeading = rwy->headingDeg();
436         headingError = fabs(heading - rwyHeading);
437         if (headingError > 180)
438             headingError = fabs(headingError - 360);
439         if (headingError < bestError) {
440             runway = (*i);
441             bestError = headingError;
442         }
443     }
444     //cerr << "Using active runway " << runway << " for heading " << heading << endl;
445     return runway;
446 }
447
448 void FGAirportDynamics::getActiveRunway(const string & trafficType,
449                                         int action, string & runway,
450                                         double heading)
451 {
452     bool ok = innerGetActiveRunway(trafficType, action, runway, heading);
453     if (!ok) {
454         runway = chooseRunwayFallback();
455     }
456 }
457
458 string FGAirportDynamics::chooseRunwayFallback()
459 {
460     FGRunway *rwy = _ap->getActiveRunwayForUsage();
461     return rwy->ident();
462 }
463
464 void FGAirportDynamics::addParking(FGParking & park)
465 {
466     parkings.push_back(park);
467 }
468
469 double FGAirportDynamics::getLatitude() const
470 {
471     return _ap->getLatitude();
472 }
473
474 double FGAirportDynamics::getLongitude() const
475 {
476     return _ap->getLongitude();
477 }
478
479 double FGAirportDynamics::getElevation() const
480 {
481     return _ap->getElevation();
482 }
483
484 const string & FGAirportDynamics::getId() const
485 {
486     return _ap->getId();
487 }
488
489 // Experimental: Return a different ground frequency depending on the leg of the
490 // Flight. Leg should always have a minimum value of two when this function is called. 
491 // Note that in this scheme, the assignment of various frequencies to various ground 
492 // operations is completely arbitrary. As such, is a short cut I need to take now,
493 // so that at least I can start working on assigning different frequencies to different
494 // operations.
495
496 int FGAirportDynamics::getGroundFrequency(unsigned leg)
497 {
498     //return freqGround.size() ? freqGround[0] : 0; };
499     //cerr << "Getting frequency for : " << leg << endl;
500     int groundFreq = 0;
501     if (leg < 1) {
502         SG_LOG(SG_ATC, SG_ALERT,
503                "Leg value is smaller than one at " << SG_ORIGIN);
504     }
505     if (freqGround.size() == 0) {
506         return 0;
507     }
508
509     if ((freqGround.size() < leg) && (leg > 0)) {
510         groundFreq =
511             (freqGround.size() <=
512              (leg - 1)) ? freqGround[freqGround.size() -
513                                      1] : freqGround[leg - 1];
514     }
515     if ((freqGround.size() >= leg) && (leg > 0)) {
516         groundFreq = freqGround[leg - 1];
517     }
518     return groundFreq;
519 }
520
521 int FGAirportDynamics::getTowerFrequency(unsigned nr)
522 {
523     int towerFreq = 0;
524     if (nr < 2) {
525         SG_LOG(SG_ATC, SG_ALERT,
526                "Leg value is smaller than two at " << SG_ORIGIN);
527     }
528     if (freqTower.size() == 0) {
529         return 0;
530     }
531     if ((freqTower.size() > nr - 1) && (nr > 1)) {
532         towerFreq = freqTower[nr - 1];
533     }
534     if ((freqTower.size() < nr - 1) && (nr > 1)) {
535         towerFreq =
536             (freqTower.size() <
537              (nr - 1)) ? freqTower[freqTower.size() -
538                                      1] : freqTower[nr - 2];
539     }
540     if ((freqTower.size() >= nr - 1) && (nr > 1)) {
541         towerFreq = freqTower[nr - 2];
542     }
543     return towerFreq;
544 }
545
546
547 FGAIFlightPlan *FGAirportDynamics::getSID(string activeRunway,
548                                           double heading)
549 {
550     return SIDs.getBest(activeRunway, heading);
551 }
552
553 const std::string FGAirportDynamics::getAtisSequence()
554 {
555    if (atisSequenceIndex == -1) {
556        updateAtisSequence(1, false);
557    }
558    
559    return GetPhoneticLetter(atisSequenceIndex);
560 }
561
562 int FGAirportDynamics::updateAtisSequence(int interval, bool forceUpdate)
563 {
564     double now = globals->get_sim_time_sec();
565     if (atisSequenceIndex == -1) {
566         // first computation
567         atisSequenceTimeStamp = now;
568         atisSequenceIndex = rand() % LTRS; // random initial sequence letters
569         return atisSequenceIndex;
570     }
571
572     int steps = static_cast<int>((now - atisSequenceTimeStamp) / interval);
573     atisSequenceTimeStamp += (interval * steps);
574     if (forceUpdate && (steps == 0)) {
575         ++steps; // a "special" ATIS update is required
576     } 
577     
578     atisSequenceIndex = (atisSequenceIndex + steps) % LTRS;
579     // return a huge value if no update occurred
580     return (atisSequenceIndex + (steps ? 0 : LTRS*1000));
581 }