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