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