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