]> git.mxchange.org Git - flightgear.git/blob - src/Airports/dynamics.cxx
Adding some more intelligence to the AI system step 2: Added a system to
[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 <plib/sg.h>
30 #include <plib/ul.h>
31
32 #include <Environment/environment_mgr.hxx>
33 #include <Environment/environment.hxx>
34 #include <simgear/misc/sg_path.hxx>
35 #include <simgear/props/props.hxx>
36 #include <simgear/structure/subsystem_mgr.hxx>
37 #include <simgear/debug/logstream.hxx>
38 #include <simgear/route/waypoint.hxx>
39 #include <Main/globals.hxx>
40 #include <Main/fg_props.hxx>
41 #include <Airports/runways.hxx>
42
43 #include STL_STRING
44 #include <vector>
45
46 SG_USING_STD(string);
47 SG_USING_STD(vector);
48 SG_USING_STD(sort);
49 SG_USING_STD(random_shuffle);
50
51 #include "dynamics.hxx"
52
53 /********** FGAirport Dynamics *********************************************/
54
55 FGAirportDynamics::FGAirportDynamics(double lat, double lon, double elev, string id) :
56   _longitude(lon),
57   _latitude(lat),
58   _elevation(elev),
59   _id(id)
60 {
61   lastUpdate = 0;
62   for (int i = 0; i < 10; i++)
63     {
64       avWindHeading [i] = 0;
65       avWindSpeed   [i] = 0;
66     }
67 }
68
69
70 // Note that the ground network should also be copied
71 FGAirportDynamics::FGAirportDynamics(const FGAirportDynamics& other) 
72 {
73   for (FGParkingVecConstIterator ip= other.parkings.begin(); ip != other.parkings.end(); ip++)
74     parkings.push_back(*(ip));
75   rwyPrefs = other.rwyPrefs;
76   lastUpdate = other.lastUpdate;
77   
78   stringVecConstIterator il;
79   for (il = other.landing.begin(); il != other.landing.end(); il++)
80     landing.push_back(*il);
81   for (il = other.takeoff.begin(); il != other.takeoff.end(); il++)
82     takeoff.push_back(*il);
83   lastUpdate = other.lastUpdate;
84   for (int i = 0; i < 10; i++)
85     {
86       avWindHeading [i] = other.avWindHeading[i];
87       avWindSpeed   [i] = other.avWindSpeed  [i];
88     }
89 }
90
91 // Destructor
92 FGAirportDynamics::~FGAirportDynamics()
93 {
94   
95 }
96
97
98 // Initialization required after XMLRead
99 void FGAirportDynamics::init() 
100 {
101   // This may seem a bit weird to first randomly shuffle the parkings
102   // and then sort them again. However, parkings are sorted here by ascending 
103   // radius. Since many parkings have similar radii, with each radius class they will
104   // still be allocated relatively systematically. Randomizing prior to sorting will
105   // prevent any initial orderings to be destroyed, leading (hopefully) to a more 
106   // naturalistic gate assignment. 
107   random_shuffle(parkings.begin(), parkings.end());
108   sort(parkings.begin(), parkings.end());
109   // add the gate positions to the ground network. 
110   groundNetwork.addNodes(&parkings);
111   groundNetwork.init();
112 }
113
114 bool FGAirportDynamics::getAvailableParking(double *lat, double *lon, double *heading, int *gateId, double rad, const string &flType, const string &acType, const string &airline)
115 {
116   bool found = false;
117   bool available = false;
118   //string gateType;
119
120   FGParkingVecIterator i;
121 //   if (flType == "cargo")
122 //     {
123 //       gateType = "RAMP_CARGO";
124 //     }
125 //   else if (flType == "ga")
126 //     {
127 //       gateType = "RAMP_GA";
128 //     }
129 //   else gateType = "GATE";
130   
131   if (parkings.begin() == parkings.end())
132     {
133       //cerr << "Could not find parking spot at " << _id << endl;
134       *lat = _latitude;
135       *lon = _longitude;
136       *heading = 0;
137       found = true;
138     }
139   else
140     {
141       // First try finding a parking with a designated airline code
142       for (i = parkings.begin(); !(i == parkings.end() || found); i++)
143         {
144           //cerr << "Gate Id: " << i->getIndex()
145           //     << " Type  : " << i->getType()
146           //     << " Codes : " << i->getCodes()
147           //     << " Radius: " << i->getRadius()
148           //     << " Name  : " << i->getName()
149           //     << " Available: " << i->isAvailable() << endl;
150           available = true;
151           // Taken by another aircraft
152           if (!(i->isAvailable()))
153             {
154               available = false;
155               continue;
156             }
157           // No airline codes, so skip
158           if (i->getCodes().empty())
159             {
160               available = false;
161               continue;
162             }
163           else // Airline code doesn't match
164             if (i->getCodes().find(airline, 0) == string::npos)
165               {
166                 available = false;
167                 continue;
168               }
169           // Type doesn't match
170           if (i->getType() != flType)
171             {
172               available = false;
173               continue;
174             }
175           // too small
176           if (i->getRadius() < rad)
177             {
178               available = false;
179               continue;
180             }
181           
182           if (available)
183             {
184               *lat     = i->getLatitude ();
185               *lon     = i->getLongitude();
186               *heading = i->getHeading  ();
187               *gateId  = i->getIndex    ();
188               i->setAvailable(false);
189               found = true;
190             }
191         }
192       // then try again for those without codes. 
193       for (i = parkings.begin(); !(i == parkings.end() || found); i++)
194         {
195           available = true;
196           if (!(i->isAvailable()))
197             {
198               available = false;
199               continue;
200             }
201           if (!(i->getCodes().empty()))
202             {
203               if ((i->getCodes().find(airline,0) == string::npos))
204           {
205             available = false;
206             continue;
207           }
208             }
209           if (i->getType() != flType)
210             {
211               available = false;
212               continue;
213             }
214               
215           if (i->getRadius() < rad)
216             {
217               available = false;
218               continue;
219             }
220           
221           if (available)
222             {
223               *lat     = i->getLatitude ();
224               *lon     = i->getLongitude();
225               *heading = i->getHeading  ();
226               *gateId  = i->getIndex    ();
227               i->setAvailable(false);
228               found = true;
229             }
230         } 
231       // And finally once more if that didn't work. Now ignore the airline codes, as a last resort
232       for (i = parkings.begin(); !(i == parkings.end() || found); i++)
233         {
234           available = true;
235           if (!(i->isAvailable()))
236             {
237               available = false;
238               continue;
239             }
240           if (i->getType() != flType)
241             {
242               available = false;
243               continue;
244             }
245           
246           if (i->getRadius() < rad)
247             {
248               available = false;
249               continue;
250             }
251           
252           if (available)
253             {
254               *lat     = i->getLatitude ();
255               *lon     = i->getLongitude();
256               *heading = i->getHeading  ();
257               *gateId  = i->getIndex    ();
258               i->setAvailable(false);
259               found = true;
260             }
261         }
262     }
263   if (!found)
264     {
265       //cerr << "Traffic overflow at" << _id 
266       //           << ". flType = " << flType 
267       //           << ". airline = " << airline 
268       //           << " Radius = " <<rad
269       //           << endl;
270       *lat = _latitude;
271       *lon = _longitude;
272       *heading = 0;
273       *gateId  = -1;
274       //exit(1);
275     }
276   return found;
277 }
278
279 void FGAirportDynamics::getParking (int id, double *lat, double* lon, double *heading)
280 {
281   if (id < 0)
282     {
283       *lat = _latitude;
284       *lon = _longitude;
285       *heading = 0;
286     }
287   else
288     {
289       FGParkingVecIterator i = parkings.begin();
290       for (i = parkings.begin(); i != parkings.end(); i++)
291         {
292           if (id == i->getIndex())
293             {
294               *lat     = i->getLatitude();
295               *lon     = i->getLongitude();
296               *heading = i->getHeading();
297             }
298         }
299     }
300
301
302 FGParking *FGAirportDynamics::getParking(int i) 
303
304   if (i < (int)parkings.size()) 
305     return &(parkings[i]); 
306   else 
307     return 0;
308 }
309 string FGAirportDynamics::getParkingName(int i) 
310
311   if (i < (int)parkings.size() && i >= 0) 
312     return (parkings[i].getName()); 
313   else 
314     return string("overflow");
315 }
316 void FGAirportDynamics::releaseParking(int id)
317 {
318   if (id >= 0)
319     {
320       
321       FGParkingVecIterator i = parkings.begin();
322       for (i = parkings.begin(); i != parkings.end(); i++)
323         {
324           if (id == i->getIndex())
325             {
326               i -> setAvailable(true);
327             }
328         }
329     }
330 }
331   
332 void  FGAirportDynamics::startXML () {
333   //cout << "Start XML" << endl;
334 }
335
336 void  FGAirportDynamics::endXML () {
337   //cout << "End XML" << endl;
338 }
339
340 void  FGAirportDynamics::startElement (const char * name, const XMLAttributes &atts) {
341   // const char *attval;
342   FGParking park;
343   FGTaxiNode taxiNode;
344   FGTaxiSegment taxiSegment;
345   int index = 0;
346   taxiSegment.setIndex(index);
347   //cout << "Start element " << name << endl;
348   string attname;
349   string value;
350   string gateName;
351   string gateNumber;
352   string lat;
353   string lon;
354   if (name == string("Parking"))
355     {
356       for (int i = 0; i < atts.size(); i++)
357         {
358           //cout << "  " << atts.getName(i) << '=' << atts.getValue(i) << endl; 
359           attname = atts.getName(i);
360           if (attname == string("index"))
361             park.setIndex(atoi(atts.getValue(i)));
362           else if (attname == string("type"))
363             park.setType(atts.getValue(i));
364          else if (attname == string("name"))
365            gateName = atts.getValue(i);
366           else if (attname == string("number"))
367             gateNumber = atts.getValue(i);
368           else if (attname == string("lat"))
369            park.setLatitude(atts.getValue(i));
370           else if (attname == string("lon"))
371             park.setLongitude(atts.getValue(i)); 
372           else if (attname == string("heading"))
373             park.setHeading(atof(atts.getValue(i)));
374           else if (attname == string("radius")) {
375             string radius = atts.getValue(i);
376             if (radius.find("M") != string::npos)
377               radius = radius.substr(0, radius.find("M",0));
378             //cerr << "Radius " << radius <<endl;
379             park.setRadius(atof(radius.c_str()));
380           }
381            else if (attname == string("airlineCodes"))
382              park.setCodes(atts.getValue(i));
383         }
384       park.setName((gateName+gateNumber));
385       parkings.push_back(park);
386     }
387   if (name == string("node")) 
388     {
389       for (int i = 0; i < atts.size() ; i++)
390         {
391           attname = atts.getName(i);
392           if (attname == string("index"))
393             taxiNode.setIndex(atoi(atts.getValue(i)));
394           if (attname == string("lat"))
395             taxiNode.setLatitude(atts.getValue(i));
396           if (attname == string("lon"))
397             taxiNode.setLongitude(atts.getValue(i));
398         }
399       groundNetwork.addNode(taxiNode);
400     }
401   if (name == string("arc")) 
402     {
403       taxiSegment.setIndex(++index);
404       for (int i = 0; i < atts.size() ; i++)
405         {
406           attname = atts.getName(i);
407           if (attname == string("begin"))
408             taxiSegment.setStartNodeRef(atoi(atts.getValue(i)));
409           if (attname == string("end"))
410             taxiSegment.setEndNodeRef(atoi(atts.getValue(i)));
411         }
412       groundNetwork.addSegment(taxiSegment);
413     }
414   // sort by radius, in asending order, so that smaller gates are first in the list
415 }
416
417 void  FGAirportDynamics::endElement (const char * name) {
418   //cout << "End element " << name << endl;
419
420 }
421
422 void  FGAirportDynamics::data (const char * s, int len) {
423   string token = string(s,len);
424   //cout << "Character data " << string(s,len) << endl;
425   //if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos))
426     //value += token;
427   //else
428     //value = string("");
429 }
430
431 void  FGAirportDynamics::pi (const char * target, const char * data) {
432   //cout << "Processing instruction " << target << ' ' << data << endl;
433 }
434
435 void  FGAirportDynamics::warning (const char * message, int line, int column) {
436   SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
437 }
438
439 void  FGAirportDynamics::error (const char * message, int line, int column) {
440   SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')');
441 }
442
443 void FGAirportDynamics::setRwyUse(const FGRunwayPreference& ref)
444 {
445   rwyPrefs = ref;
446   //cerr << "Exiting due to not implemented yet" << endl;
447   //exit(1);
448 }
449 void FGAirportDynamics::getActiveRunway(const string &trafficType, int action, string &runway)
450 {
451   double windSpeed;
452   double windHeading;
453   double maxTail;
454   double maxCross;
455   string name;
456   string type;
457
458   if (!(rwyPrefs.available()))
459     {
460       runway = chooseRunwayFallback();
461       return; // generic fall back goes here
462     }
463   else
464     {
465       RunwayGroup *currRunwayGroup = 0;
466       int nrActiveRunways = 0;
467       time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
468       if ((abs((long)(dayStart - lastUpdate)) > 600) || trafficType != prevTrafficType)
469         {
470           landing.clear();
471           takeoff.clear();
472           lastUpdate = dayStart;
473           prevTrafficType = trafficType;
474
475           FGEnvironment 
476             stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
477             ->getEnvironment(getLatitude(), 
478                              getLongitude(), 
479                              getElevation());
480           
481           windSpeed = stationweather.get_wind_speed_kt();
482           windHeading = stationweather.get_wind_from_heading_deg();
483          //  double averageWindSpeed   = 0;
484 //        double averageWindHeading = 0;
485 //        double cosHeading         = 0;
486 //        double sinHeading         = 0;
487 //        // Initialize at the beginning of the next day or startup
488 //        if ((lastUpdate == 0) || (dayStart < lastUpdate))
489 //          {
490 //            for (int i = 0; i < 10; i++)
491 //              {
492 //                avWindHeading [i] = windHeading;
493 //                avWindSpeed   [i] = windSpeed;
494 //              }
495 //          }
496 //        else
497 //          {
498 //            if (windSpeed != avWindSpeed[9]) // update if new metar data 
499 //              {
500 //                // shift the running average
501 //                for (int i = 0; i < 9 ; i++)
502 //                  {
503 //                    avWindHeading[i] = avWindHeading[i+1];
504 //                    avWindSpeed  [i] = avWindSpeed  [i+1];
505 //                  }
506 //              } 
507 //            avWindHeading[9] = windHeading;
508 //            avWindSpeed  [9] = windSpeed;
509 //          }
510           
511 //        for (int i = 0; i < 10; i++)
512 //          {
513 //            averageWindSpeed   += avWindSpeed   [i];
514 //            //averageWindHeading += avWindHeading [i];
515 //            cosHeading += cos(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
516 //            sinHeading += sin(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
517 //          }
518 //        averageWindSpeed   /= 10;
519 //        //averageWindHeading /= 10;
520 //        cosHeading /= 10;
521 //        sinHeading /= 10;
522 //        averageWindHeading = atan2(sinHeading, cosHeading) *SG_RADIANS_TO_DEGREES;
523 //        if (averageWindHeading < 0)
524 //          averageWindHeading += 360.0;
525 //        //cerr << "Wind Heading " << windHeading << " average " << averageWindHeading << endl;
526 //        //cerr << "Wind Speed   " << windSpeed   << " average " << averageWindSpeed   << endl;
527 //        lastUpdate = dayStart;
528 //            //if (wind_speed == 0) {
529 //        //  wind_heading = 270;        This forces West-facing rwys to be used in no-wind situations
530 //          // which is consistent with Flightgear's initial setup.
531 //        //}
532           
533           //string rwy_no = globals->get_runways()->search(apt->getId(), int(wind_heading));
534           string scheduleName;
535           //cerr << "finding active Runway for" << _id << endl;
536           //cerr << "Nr of seconds since day start << " << dayStart << endl;
537
538           ScheduleTime *currSched;
539           //cerr << "A"<< endl;
540           currSched = rwyPrefs.getSchedule(trafficType.c_str());
541           if (!(currSched))
542             return;   
543           //cerr << "B"<< endl;
544           scheduleName = currSched->getName(dayStart);
545           maxTail  = currSched->getTailWind  ();
546           maxCross = currSched->getCrossWind ();
547           //cerr << "SChedule anme = " << scheduleName << endl;
548           if (scheduleName.empty())
549             return;
550           //cerr << "C"<< endl;
551           currRunwayGroup = rwyPrefs.getGroup(scheduleName); 
552           //cerr << "D"<< endl;
553           if (!(currRunwayGroup))
554             return;
555           nrActiveRunways = currRunwayGroup->getNrActiveRunways();
556           //cerr << "Nr of Active Runways = " << nrActiveRunways << endl; 
557
558           // 
559           currRunwayGroup->setActive(_id, 
560                                      windSpeed, 
561                                      windHeading, 
562                                      maxTail, 
563                                      maxCross, 
564                                      &currentlyActive); 
565
566           // Note that I SHOULD keep three lists in memory, one for 
567           // general aviation, one for commercial and one for military
568           // traffic.
569           currentlyActive.clear();
570           nrActiveRunways = currRunwayGroup->getNrActiveRunways();
571           for (int i = 0; i < nrActiveRunways; i++)
572             {
573               type = "unknown"; // initialize to something other than landing or takeoff
574               currRunwayGroup->getActive(i, name, type);
575               if (type == "landing")
576                 {
577                   landing.push_back(name);
578                   currentlyActive.push_back(name);
579                   //cerr << "Landing " << name << endl; 
580                 }
581               if (type == "takeoff")
582                 {
583                   takeoff.push_back(name);
584                   currentlyActive.push_back(name);
585                   //cerr << "takeoff " << name << endl;
586                 }
587             }
588         }
589       if (action == 1) // takeoff 
590         {
591           int nr = takeoff.size();
592           if (nr)
593             {
594               // Note that the randomization below, is just a placeholder to choose between
595               // multiple active runways for this action. This should be
596               // under ATC control.
597               runway = takeoff[(rand() %  nr)];
598             }
599           else
600             { // Fallback
601               runway = chooseRunwayFallback();
602             }
603         } 
604       if (action == 2) // landing
605         {
606           int nr = landing.size();
607           if (nr)
608             {
609               runway = landing[(rand() % nr)];
610             }
611           else
612             {  //fallback
613                runway = chooseRunwayFallback();
614             }
615         }
616       
617       //runway = globals->get_runways()->search(_id, int(windHeading));
618       //cerr << "Seleceted runway: " << runway << endl;
619     }
620 }
621
622 string FGAirportDynamics::chooseRunwayFallback()
623 {   
624   FGEnvironment 
625     stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
626     ->getEnvironment(getLatitude(), 
627                      getLongitude(),
628                      getElevation());
629   
630   double windSpeed = stationweather.get_wind_speed_kt();
631   double windHeading = stationweather.get_wind_from_heading_deg();
632   if (windSpeed == 0) {
633     windHeading = 270;  // This forces West-facing rwys to be used in no-wind situations
634     //which is consistent with Flightgear's initial setup.
635   }
636   
637    return globals->get_runways()->search(_id, int(windHeading));
638 }