]> git.mxchange.org Git - flightgear.git/blob - src/Airports/dynamics.cxx
Fix for refueling and radar calculations.
[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 // Initialization required after XMLRead
98 void FGAirportDynamics::init() 
99 {
100   // This may seem a bit weird to first randomly shuffle the parkings
101   // and then sort them again. However, parkings are sorted here by ascending 
102   // radius. Since many parkings have similar radii, with each radius class they will
103   // still be allocated relatively systematically. Randomizing prior to sorting will
104   // prevent any initial orderings to be destroyed, leading (hopefully) to a more 
105   // naturalistic gate assignment. 
106   random_shuffle(parkings.begin(), parkings.end());
107   sort(parkings.begin(), parkings.end());
108   // add the gate positions to the ground network. 
109   groundNetwork.addNodes(&parkings);
110   groundNetwork.init();
111   groundNetwork .setTowerController(&towerController);
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             {
165               //cerr << "Code = " << airline << ": Codes " << i->getCodes();
166               if (i->getCodes().find(airline, 0) == string::npos)
167                 {
168                   available = false;
169                   //cerr << "Unavailable" << endl;
170                   continue;
171                 }
172               else
173                 {
174                   //cerr << "Available" << endl;
175                 }
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->getHeading();
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   SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
445 }
446
447 void  FGAirportDynamics::error (const char * message, int line, int column) {
448   SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')');
449 }
450
451 void FGAirportDynamics::setRwyUse(const FGRunwayPreference& ref)
452 {
453   rwyPrefs = ref;
454   //cerr << "Exiting due to not implemented yet" << endl;
455   //exit(1);
456 }
457 void FGAirportDynamics::getActiveRunway(const string &trafficType, int action, string &runway)
458 {
459   double windSpeed;
460   double windHeading;
461   double maxTail;
462   double maxCross;
463   string name;
464   string type;
465
466   if (!(rwyPrefs.available()))
467     {
468       runway = chooseRunwayFallback();
469       return; // generic fall back goes here
470     }
471   else
472     {
473       RunwayGroup *currRunwayGroup = 0;
474       int nrActiveRunways = 0;
475       time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
476       if ((abs((long)(dayStart - lastUpdate)) > 600) || trafficType != prevTrafficType)
477         {
478           landing.clear();
479           takeoff.clear();
480           lastUpdate = dayStart;
481           prevTrafficType = trafficType;
482
483           FGEnvironment 
484             stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
485             ->getEnvironment(getLatitude(), 
486                              getLongitude(), 
487                              getElevation());
488           
489           windSpeed = stationweather.get_wind_speed_kt();
490           windHeading = stationweather.get_wind_from_heading_deg();
491          //  double averageWindSpeed   = 0;
492 //        double averageWindHeading = 0;
493 //        double cosHeading         = 0;
494 //        double sinHeading         = 0;
495 //        // Initialize at the beginning of the next day or startup
496 //        if ((lastUpdate == 0) || (dayStart < lastUpdate))
497 //          {
498 //            for (int i = 0; i < 10; i++)
499 //              {
500 //                avWindHeading [i] = windHeading;
501 //                avWindSpeed   [i] = windSpeed;
502 //              }
503 //          }
504 //        else
505 //          {
506 //            if (windSpeed != avWindSpeed[9]) // update if new metar data 
507 //              {
508 //                // shift the running average
509 //                for (int i = 0; i < 9 ; i++)
510 //                  {
511 //                    avWindHeading[i] = avWindHeading[i+1];
512 //                    avWindSpeed  [i] = avWindSpeed  [i+1];
513 //                  }
514 //              } 
515 //            avWindHeading[9] = windHeading;
516 //            avWindSpeed  [9] = windSpeed;
517 //          }
518           
519 //        for (int i = 0; i < 10; i++)
520 //          {
521 //            averageWindSpeed   += avWindSpeed   [i];
522 //            //averageWindHeading += avWindHeading [i];
523 //            cosHeading += cos(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
524 //            sinHeading += sin(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
525 //          }
526 //        averageWindSpeed   /= 10;
527 //        //averageWindHeading /= 10;
528 //        cosHeading /= 10;
529 //        sinHeading /= 10;
530 //        averageWindHeading = atan2(sinHeading, cosHeading) *SG_RADIANS_TO_DEGREES;
531 //        if (averageWindHeading < 0)
532 //          averageWindHeading += 360.0;
533 //        //cerr << "Wind Heading " << windHeading << " average " << averageWindHeading << endl;
534 //        //cerr << "Wind Speed   " << windSpeed   << " average " << averageWindSpeed   << endl;
535 //        lastUpdate = dayStart;
536 //            //if (wind_speed == 0) {
537 //        //  wind_heading = 270;        This forces West-facing rwys to be used in no-wind situations
538 //          // which is consistent with Flightgear's initial setup.
539 //        //}
540           
541           //string rwy_no = globals->get_runways()->search(apt->getId(), int(wind_heading));
542           string scheduleName;
543           //cerr << "finding active Runway for" << _id << endl;
544           //cerr << "Nr of seconds since day start << " << dayStart << endl;
545
546           ScheduleTime *currSched;
547           //cerr << "A"<< endl;
548           currSched = rwyPrefs.getSchedule(trafficType.c_str());
549           if (!(currSched))
550             return;   
551           //cerr << "B"<< endl;
552           scheduleName = currSched->getName(dayStart);
553           maxTail  = currSched->getTailWind  ();
554           maxCross = currSched->getCrossWind ();
555           //cerr << "SChedule anme = " << scheduleName << endl;
556           if (scheduleName.empty())
557             return;
558           //cerr << "C"<< endl;
559           currRunwayGroup = rwyPrefs.getGroup(scheduleName); 
560           //cerr << "D"<< endl;
561           if (!(currRunwayGroup))
562             return;
563           nrActiveRunways = currRunwayGroup->getNrActiveRunways();
564           //cerr << "Nr of Active Runways = " << nrActiveRunways << endl; 
565
566           // 
567           currRunwayGroup->setActive(_id, 
568                                      windSpeed, 
569                                      windHeading, 
570                                      maxTail, 
571                                      maxCross, 
572                                      &currentlyActive); 
573
574           // Note that I SHOULD keep three lists in memory, one for 
575           // general aviation, one for commercial and one for military
576           // traffic.
577           currentlyActive.clear();
578           nrActiveRunways = currRunwayGroup->getNrActiveRunways();
579           for (int i = 0; i < nrActiveRunways; i++)
580             {
581               type = "unknown"; // initialize to something other than landing or takeoff
582               currRunwayGroup->getActive(i, name, type);
583               if (type == "landing")
584                 {
585                   landing.push_back(name);
586                   currentlyActive.push_back(name);
587                   //cerr << "Landing " << name << endl; 
588                 }
589               if (type == "takeoff")
590                 {
591                   takeoff.push_back(name);
592                   currentlyActive.push_back(name);
593                   //cerr << "takeoff " << name << endl;
594                 }
595             }
596         }
597       if (action == 1) // takeoff 
598         {
599           int nr = takeoff.size();
600           if (nr)
601             {
602               // Note that the randomization below, is just a placeholder to choose between
603               // multiple active runways for this action. This should be
604               // under ATC control.
605               runway = takeoff[(rand() %  nr)];
606             }
607           else
608             { // Fallback
609               runway = chooseRunwayFallback();
610             }
611         } 
612       if (action == 2) // landing
613         {
614           int nr = landing.size();
615           if (nr)
616             {
617               runway = landing[(rand() % nr)];
618             }
619           else
620             {  //fallback
621                runway = chooseRunwayFallback();
622             }
623         }
624       
625       //runway = globals->get_runways()->search(_id, int(windHeading));
626       //cerr << "Seleceted runway: " << runway << endl;
627     }
628 }
629
630 string FGAirportDynamics::chooseRunwayFallback()
631 {   
632   FGEnvironment 
633     stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
634     ->getEnvironment(getLatitude(), 
635                      getLongitude(),
636                      getElevation());
637   
638   double windSpeed = stationweather.get_wind_speed_kt();
639   double windHeading = stationweather.get_wind_from_heading_deg();
640   if (windSpeed == 0) {
641     windHeading = 270;  // This forces West-facing rwys to be used in no-wind situations
642     //which is consistent with Flightgear's initial setup.
643   }
644   
645    return globals->get_runways()->search(_id, int(windHeading));
646 }