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