2 // simple.cxx -- a really simplistic class to manage airport ID,
3 // lat, lon of the center of one of it's runways, and
6 // Written by Curtis Olson, started April 1998.
7 // Updated by Durk Talsma, started December, 2004.
9 // Copyright (C) 1998 Curtis L. Olson - http://www.flightgear.org/~curt
11 // This program is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU General Public License as
13 // published by the Free Software Foundation; either version 2 of the
14 // License, or (at your option) any later version.
16 // This program is distributed in the hope that it will be useful, but
17 // WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 // General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32 # define _USE_MATH_DEFINES
37 #include <simgear/compiler.h>
42 #include <Environment/environment_mgr.hxx>
43 #include <Environment/environment.hxx>
44 #include <simgear/misc/sg_path.hxx>
45 #include <simgear/props/props.hxx>
46 #include <simgear/structure/subsystem_mgr.hxx>
47 #include <simgear/debug/logstream.hxx>
48 #include <Main/globals.hxx>
49 #include <Main/fg_props.hxx>
50 #include <Airports/runways.hxx>
57 SG_USING_STD(random_shuffle);
59 /******************************************************************************
61 ***************e*************************************************************/
62 void ScheduleTime::clear()
66 scheduleNames.clear();
70 ScheduleTime::ScheduleTime(const ScheduleTime &other)
73 timeVecConstIterator i;
74 for (i = other.start.begin(); i != other.start.end(); i++)
76 for (i = other.end.begin(); i != other.end.end(); i++)
78 stringVecConstIterator k;
79 for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); k++)
80 scheduleNames.push_back(*k);
83 //stringVec scheduleNames;
84 tailWind = other.tailWind;
85 crssWind = other.tailWind;
89 ScheduleTime & ScheduleTime::operator= (const ScheduleTime &other)
93 timeVecConstIterator i;
94 for (i = other.start.begin(); i != other.start.end(); i++)
96 for (i = other.end.begin(); i != other.end.end(); i++)
98 stringVecConstIterator k;
99 for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); k++)
100 scheduleNames.push_back(*k);
103 //stringVec scheduleNames;
104 tailWind = other.tailWind;
105 crssWind = other.tailWind;
108 string ScheduleTime::getName(time_t dayStart)
110 if ((start.size() != end.size()) || (start.size() != scheduleNames.size()))
112 cerr << "Unable to parse schedule times" << endl;
117 int nrItems = start.size();
118 //cerr << "Nr of items to process: " << nrItems << endl;
121 for (unsigned int i = 0; i < start.size(); i++)
124 if ((dayStart >= start[i]) && (dayStart <= end[i]))
125 return scheduleNames[i];
128 //couldn't find one so return 0;
129 //cerr << "Returning 0 " << endl;
133 /******************************************************************************
135 *****************************************************************************/
137 RunwayList::RunwayList(const RunwayList &other)
140 stringVecConstIterator i;
141 for (i = other.preferredRunways.begin(); i != other.preferredRunways.end(); i++)
142 preferredRunways.push_back(*i);
144 RunwayList& RunwayList::operator= (const RunwayList &other)
147 preferredRunways.clear();
148 stringVecConstIterator i;
149 for (i = other.preferredRunways.begin(); i != other.preferredRunways.end(); i++)
150 preferredRunways.push_back(*i);
153 void RunwayList::set(string tp, string lst)
155 //weekday = atoi(timeCopy.substr(0,1).c_str());
156 // timeOffsetInDays = weekday - currTimeDate->getGmt()->tm_wday;
157 // timeCopy = timeCopy.substr(2,timeCopy.length());
161 while (rwys.find(",") != string::npos)
163 rwy = rwys.substr(0, rwys.find(",",0));
164 //cerr << "adding runway [" << rwy << "] to the list " << endl;
165 preferredRunways.push_back(rwy);
166 rwys.erase(0, rwys.find(",",0)+1); // erase until after the first whitspace
167 while (rwys[0] == ' ')
168 rwys.erase(0, 1); // Erase any leading whitespaces.
169 //cerr << "Remaining runway list " << rwys;
171 preferredRunways.push_back(rwys);
175 void RunwayList::clear()
178 preferredRunways.clear();
180 /****************************************************************************
182 ***************************************************************************/
184 RunwayGroup::RunwayGroup(const RunwayGroup &other)
187 RunwayListVecConstIterator i;
188 for (i = other.rwyList.begin(); i != other.rwyList.end(); i++)
189 rwyList.push_back(*i);
190 choice[0] = other.choice[0];
191 choice[1] = other.choice[1];
192 nrActive = other.nrActive;
194 RunwayGroup& RunwayGroup:: operator= (const RunwayGroup &other)
198 RunwayListVecConstIterator i;
199 for (i = other.rwyList.begin(); i != other.rwyList.end(); i++)
200 rwyList.push_back(*i);
201 choice[0] = other.choice[0];
202 choice[1] = other.choice[1];
203 nrActive = other.nrActive;
207 void RunwayGroup::setActive(string aptId,
215 int activeRwys = rwyList.size(); // get the number of runways active
217 // bool found = true;
226 nrOfPreferences = rwyList[0].getRwyList()->size();
227 for (int i = 0; i < nrOfPreferences; i++)
229 bool validSelection = true;
230 for (int j = 0; j < activeRwys; j++)
232 //cerr << "I J " << i << " " << j << endl;
233 name = rwyList[j].getRwyList(i);
234 //cerr << "Name of Runway: " << name << endl;
235 if (globals->get_runways()->search( aptId,
239 //cerr << "Succes" << endl;
240 hdgDiff = fabs(windHeading - rwy._heading);
241 //cerr << "Wind Heading: " << windHeading << "Runway Heading: " <<rwy._heading << endl;
242 //cerr << "Wind Speed : " << windSpeed << endl;
244 hdgDiff = 360 - hdgDiff;
245 //cerr << "Heading diff: " << hdgDiff << endl;
246 hdgDiff *= ((2*M_PI)/360.0); // convert to radians
247 crossWind = windSpeed * sin(hdgDiff);
248 tailWind = -windSpeed * cos(hdgDiff);
249 //cerr << "Tailwind : " << tailWind << endl;
250 //cerr << "Crosswnd : " << crossWind << endl;
251 if ((tailWind > maxTail) || (crossWind > maxCross))
252 validSelection = false;
254 cerr << "Failed to find runway " << name << " at " << aptId << endl;
261 //cerr << "Valid runay selection : " << i << endl;
262 nrActive = activeRwys;
267 // If this didn't work, due to heavy winds, try again
268 // but select only one landing and one takeoff runway.
271 for (int i = activeRwys-1; i; i--)
273 if (rwyList[i].getType() == string("landing"))
275 if (rwyList[i].getType() == string("takeoff"))
278 //cerr << "Choosing " << choice[0] << " for landing and " << choice[1] << "for takeoff" << endl;
279 nrOfPreferences = rwyList[0].getRwyList()->size();
280 for (int i = 0; i < nrOfPreferences; i++)
282 bool validSelection = true;
283 for (int j = 0; j < 2; j++)
285 //cerr << "I J " << i << " " << j << endl;
286 name = rwyList[choice[j]].getRwyList(i);
287 //cerr << "Name of Runway: " << name << endl;
288 if (globals->get_runways()->search( aptId,
292 //cerr << "Succes" << endl;
293 hdgDiff = fabs(windHeading - rwy._heading);
294 //cerr << "Wind Heading: " << windHeading << "Runway Heading: " <<rwy._heading << endl;
295 //cerr << "Wind Speed : " << windSpeed << endl;
297 hdgDiff = 360 - hdgDiff;
298 //cerr << "Heading diff: " << hdgDiff << endl;
299 hdgDiff *= ((2*M_PI)/360.0); // convert to radians
300 crossWind = windSpeed * sin(hdgDiff);
301 tailWind = -windSpeed * cos(hdgDiff);
302 //cerr << "Tailwind : " << tailWind << endl;
303 //cerr << "Crosswnd : " << crossWind << endl;
304 if ((tailWind > maxTail) || (crossWind > maxCross))
305 validSelection = false;
307 cerr << "Failed to find runway " << name << " at " << aptId << endl;
314 //cerr << "Valid runay selection : " << i << endl;
322 //RunwayListVectorIterator i; // = rwlist.begin();
323 //stringVecIterator j;
324 //for (i = rwyList.begin(); i != rwyList.end(); i++)
326 // cerr << i->getType();
327 // for (j = i->getRwyList()->begin(); j != i->getRwyList()->end(); j++)
337 void RunwayGroup::getActive(int i, string *name, string *type)
343 if (nrActive == (int)rwyList.size())
345 *name = rwyList[i].getRwyList(active);
346 *type = rwyList[i].getType();
350 *name = rwyList[choice[i]].getRwyList(active);
351 *type = rwyList[choice[i]].getType();
354 /*****************************************************************************
355 * FGRunway preference
356 ****************************************************************************/
357 FGRunwayPreference::FGRunwayPreference()
359 //cerr << "Running default Constructor" << endl;
363 FGRunwayPreference::FGRunwayPreference(const FGRunwayPreference &other)
365 initialized = other.initialized;
367 scheduleName = other.scheduleName;
369 comTimes = other.comTimes; // Commercial Traffic;
370 genTimes = other.genTimes; // General Aviation;
371 milTimes = other.milTimes; // Military Traffic;
372 currTimes= other.currTimes; // Needed for parsing;
374 rwyList = other.rwyList;
375 rwyGroup = other.rwyGroup;
376 PreferenceListConstIterator i;
377 for (i = other.preferences.begin(); i != other.preferences.end(); i++)
378 preferences.push_back(*i);
381 FGRunwayPreference & FGRunwayPreference::operator= (const FGRunwayPreference &other)
383 initialized = other.initialized;
385 scheduleName = other.scheduleName;
387 comTimes = other.comTimes; // Commercial Traffic;
388 genTimes = other.genTimes; // General Aviation;
389 milTimes = other.milTimes; // Military Traffic;
390 currTimes= other.currTimes; // Needed for parsing;
392 rwyList = other.rwyList;
393 rwyGroup = other.rwyGroup;
394 PreferenceListConstIterator i;
396 for (i = other.preferences.begin(); i != other.preferences.end(); i++)
397 preferences.push_back(*i);
401 ScheduleTime *FGRunwayPreference::getSchedule(const char *trafficType)
403 if (!(strcmp(trafficType, "com"))) {
406 if (!(strcmp(trafficType, "gen"))) {
409 if (!(strcmp(trafficType, "mil"))) {
415 RunwayGroup *FGRunwayPreference::getGroup(const string groupName)
417 PreferenceListIterator i = preferences.begin();
418 if (preferences.begin() == preferences.end())
420 while (!(i == preferences.end() || i->getName() == groupName))
422 if (i != preferences.end())
428 void FGRunwayPreference::startXML () {
429 // cout << "Start XML" << endl;
432 void FGRunwayPreference::endXML () {
433 cout << "End XML" << endl;
436 void FGRunwayPreference::startElement (const char * name, const XMLAttributes &atts) {
437 //cout << "StartElement " << name << endl;
439 if (!(strcmp(name, "wind"))) {
440 //cerr << "Will be processing Wind" << endl;
441 for (int i = 0; i < atts.size(); i++)
443 //cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
444 //attname = atts.getName(i);
445 if (atts.getName(i) == string("tail")) {
446 //cerr << "Tail Wind = " << atts.getValue(i) << endl;
447 currTimes.setTailWind(atof(atts.getValue(i)));
449 if (atts.getName(i) == string("cross")) {
450 //cerr << "Cross Wind = " << atts.getValue(i) << endl;
451 currTimes.setCrossWind(atof(atts.getValue(i)));
455 if (!(strcmp(name, "time"))) {
456 //cerr << "Will be processing time" << endl;
457 for (int i = 0; i < atts.size(); i++)
459 if (atts.getName(i) == string("start")) {
460 //cerr << "Start Time = " << atts.getValue(i) << endl;
461 currTimes.addStartTime(processTime(atts.getValue(i)));
463 if (atts.getName(i) == string("end")) {
464 //cerr << "End time = " << atts.getValue(i) << endl;
465 currTimes.addEndTime(processTime(atts.getValue(i)));
467 if (atts.getName(i) == string("schedule")) {
468 //cerr << "Schedule Name = " << atts.getValue(i) << endl;
469 currTimes.addScheduleName(atts.getValue(i));
473 if (!(strcmp(name, "takeoff"))) {
476 if (!(strcmp(name, "landing")))
480 if (!(strcmp(name, "schedule"))) {
481 for (int i = 0; i < atts.size(); i++)
483 //cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
484 //attname = atts.getName(i);
485 if (atts.getName(i) == string("name")) {
486 //cerr << "Schedule name = " << atts.getValue(i) << endl;
487 scheduleName = atts.getValue(i);
493 //based on a string containing hour and minute, return nr seconds since day start.
494 time_t FGRunwayPreference::processTime(string tme)
496 string hour = tme.substr(0, tme.find(":",0));
497 string minute = tme.substr(tme.find(":",0)+1, tme.length());
499 //cerr << "hour = " << hour << " Minute = " << minute << endl;
500 return (atoi(hour.c_str()) * 3600 + atoi(minute.c_str()) * 60);
503 void FGRunwayPreference::endElement (const char * name) {
504 //cout << "End element " << name << endl;
505 if (!(strcmp(name, "rwyuse"))) {
508 if (!(strcmp(name, "com"))) { // Commercial Traffic
509 //cerr << "Setting time table for commerical traffic" << endl;
510 comTimes = currTimes;
513 if (!(strcmp(name, "gen"))) { // General Aviation
514 //cerr << "Setting time table for general aviation" << endl;
515 genTimes = currTimes;
518 if (!(strcmp(name, "mil"))) { // Military Traffic
519 //cerr << "Setting time table for military traffic" << endl;
520 genTimes = currTimes;
524 if (!(strcmp(name, "takeoff"))) {
525 //cerr << "Adding takeoff: " << value << endl;
526 rwyList.set(name, value);
527 rwyGroup.add(rwyList);
529 if (!(strcmp(name, "landing"))) {
530 //cerr << "Adding landing: " << value << endl;
531 rwyList.set(name, value);
532 rwyGroup.add(rwyList);
534 if (!(strcmp(name, "schedule"))) {
535 //cerr << "Adding schedule" << scheduleName << endl;
536 rwyGroup.setName(scheduleName);
537 //rwyGroup.addRunways(rwyList);
538 preferences.push_back(rwyGroup);
544 void FGRunwayPreference::data (const char * s, int len) {
545 string token = string(s,len);
546 //cout << "Character data " << string(s,len) << endl;
547 //if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos))
550 // value = string("");
554 void FGRunwayPreference::pi (const char * target, const char * data) {
555 //cout << "Processing instruction " << target << ' ' << data << endl;
558 void FGRunwayPreference::warning (const char * message, int line, int column) {
559 cout << "Warning: " << message << " (" << line << ',' << column << ')'
563 void FGRunwayPreference::error (const char * message, int line, int column) {
564 cout << "Error: " << message << " (" << line << ',' << column << ')'
568 /*****************************************************************************
569 * Helper function for parsing position string
570 ****************************************************************************/
571 double processPosition(string pos)
580 prefix= subs.substr(0,1);
581 if (prefix == string("S") || (prefix == string("W")))
583 subs = subs.substr(1, subs.length());
584 degree = subs.substr(0, subs.find(" ",0));
585 decimal = subs.substr(subs.find(" ",0), subs.length());
588 //cerr << sign << " "<< degree << " " << decimal << endl;
589 value = sign * (atof(degree.c_str()) + atof(decimal.c_str())/60.0);
590 //cerr << value <<endl;
596 /*********************************************************************************
598 ********************************************************************************/
599 FGParking::FGParking(double lat,
614 airlineCodes = codes;
618 /***************************************************************************
620 ***************************************************************************/
621 FGAirport::FGAirport() : _longitude(0), _latitude(0), _elevation(0)
624 for (int i = 0; i < 10; i++)
626 avWindHeading [i] = 0;
631 FGAirport::FGAirport(const FGAirport& other)
634 _longitude = other._longitude;
635 _latitude = other._latitude;
636 _elevation = other._elevation;
638 _has_metar = other._has_metar;
639 for (FGParkingVecConstIterator ip= other.parkings.begin(); ip != other.parkings.end(); ip++)
640 parkings.push_back(*(ip));
641 rwyPrefs = other.rwyPrefs;
642 lastUpdate = other.lastUpdate;
644 stringVecConstIterator il;
645 for (il = other.landing.begin(); il != other.landing.end(); il++)
646 landing.push_back(*il);
647 for (il = other.takeoff.begin(); il != other.takeoff.end(); il++)
648 takeoff.push_back(*il);
649 lastUpdate = other.lastUpdate;
650 for (int i = 0; i < 10; i++)
652 avWindHeading [i] = other.avWindHeading[i];
653 avWindSpeed [i] = other.avWindSpeed [i];
657 FGAirport::FGAirport(string id, double lon, double lat, double elev, string name, bool has_metar)
664 _has_metar = has_metar;
666 for (int i = 0; i < 10; i++)
668 avWindHeading [i] = 0;
674 // Initialization required after XMLRead
675 void FGAirport::init()
677 // This may seem a bit weird to first randomly shuffle the parkings
678 // and then sort them again. However, parkings are sorted here by ascending
679 // radius. Since many parkings have similar radii, with each radius class they will
680 // still be allocated relatively systematically. Randomizing prior to sorting will
681 // prevent any initial orderings to be destroyed, leading (hopefully) to a more
682 // naturalistic gate assignment.
683 random_shuffle(parkings.begin(), parkings.end());
684 sort(parkings.begin(), parkings.end());
685 // add the gate positions to the ground network.
686 groundNetwork.addNodes(&parkings);
687 groundNetwork.init();
690 bool FGAirport::getAvailableParking(double *lat, double *lon, double *heading, int *gateId, double rad, string flType, string acType, string airline)
693 bool available = false;
696 FGParkingVecIterator i;
697 // if (flType == "cargo")
699 // gateType = "RAMP_CARGO";
701 // else if (flType == "ga")
703 // gateType = "RAMP_GA";
705 // else gateType = "GATE";
707 if (parkings.begin() == parkings.end())
709 //cerr << "Could not find parking spot at " << _id << endl;
717 // First try finding a parking with a designated airline code
718 for (i = parkings.begin(); !(i == parkings.end() || found); i++)
720 //cerr << "Gate Id: " << i->getIndex()
721 // << " Type : " << i->getType()
722 // << " Codes : " << i->getCodes()
723 // << " Radius: " << i->getRadius()
724 // << " Name : " << i->getName()
725 // << " Available: " << i->isAvailable() << endl;
727 // Taken by another aircraft
728 if (!(i->isAvailable()))
733 // No airline codes, so skip
734 if (i->getCodes().empty())
739 else // Airline code doesn't match
740 if (i->getCodes().find(airline, 0) == string::npos)
745 // Type doesn't match
746 if (i->getType() != flType)
752 if (i->getRadius() < rad)
760 *lat = i->getLatitude ();
761 *lon = i->getLongitude();
762 *heading = i->getHeading ();
763 *gateId = i->getIndex ();
764 i->setAvailable(false);
768 // then try again for those without codes.
769 for (i = parkings.begin(); !(i == parkings.end() || found); i++)
772 if (!(i->isAvailable()))
777 if (!(i->getCodes().empty()))
779 if ((i->getCodes().find(airline,0) == string::npos))
785 if (i->getType() != flType)
791 if (i->getRadius() < rad)
799 *lat = i->getLatitude ();
800 *lon = i->getLongitude();
801 *heading = i->getHeading ();
802 *gateId = i->getIndex ();
803 i->setAvailable(false);
807 // And finally once more if that didn't work. Now ignore the airline codes, as a last resort
808 for (i = parkings.begin(); !(i == parkings.end() || found); i++)
811 if (!(i->isAvailable()))
816 if (i->getType() != flType)
822 if (i->getRadius() < rad)
830 *lat = i->getLatitude ();
831 *lon = i->getLongitude();
832 *heading = i->getHeading ();
833 *gateId = i->getIndex ();
834 i->setAvailable(false);
841 //cerr << "Traffic overflow at" << _id
842 // << ". flType = " << flType
843 // << ". airline = " << airline
844 // << " Radius = " <<rad
855 void FGAirport::getParking (int id, double *lat, double* lon, double *heading)
865 FGParkingVecIterator i = parkings.begin();
866 for (i = parkings.begin(); i != parkings.end(); i++)
868 if (id == i->getIndex())
870 *lat = i->getLatitude();
871 *lon = i->getLongitude();
872 *heading = i->getLongitude();
878 FGParking *FGAirport::getParking(int i)
880 if (i < (int)parkings.size())
881 return &(parkings[i]);
885 string FGAirport::getParkingName(int i)
887 if (i < (int)parkings.size() && i >= 0)
888 return (parkings[i].getName());
890 return string("overflow");
892 void FGAirport::releaseParking(int id)
897 FGParkingVecIterator i = parkings.begin();
898 for (i = parkings.begin(); i != parkings.end(); i++)
900 if (id == i->getIndex())
902 i -> setAvailable(true);
908 void FGAirport::startXML () {
909 //cout << "Start XML" << endl;
912 void FGAirport::endXML () {
913 //cout << "End XML" << endl;
916 void FGAirport::startElement (const char * name, const XMLAttributes &atts) {
917 // const char *attval;
920 FGTaxiSegment taxiSegment;
922 taxiSegment.setIndex(index);
923 //cout << "Start element " << name << endl;
930 if (name == string("Parking"))
932 for (int i = 0; i < atts.size(); i++)
934 //cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
935 attname = atts.getName(i);
936 if (attname == string("index"))
937 park.setIndex(atoi(atts.getValue(i)));
938 else if (attname == string("type"))
939 park.setType(atts.getValue(i));
940 else if (attname == string("name"))
941 gateName = atts.getValue(i);
942 else if (attname == string("number"))
943 gateNumber = atts.getValue(i);
944 else if (attname == string("lat"))
945 park.setLatitude(atts.getValue(i));
946 else if (attname == string("lon"))
947 park.setLongitude(atts.getValue(i));
948 else if (attname == string("heading"))
949 park.setHeading(atof(atts.getValue(i)));
950 else if (attname == string("radius")) {
951 string radius = atts.getValue(i);
952 if (radius.find("M") != string::npos)
953 radius = radius.substr(0, radius.find("M",0));
954 //cerr << "Radius " << radius <<endl;
955 park.setRadius(atof(radius.c_str()));
957 else if (attname == string("airlineCodes"))
958 park.setCodes(atts.getValue(i));
960 park.setName((gateName+gateNumber));
961 parkings.push_back(park);
963 if (name == string("node"))
965 for (int i = 0; i < atts.size() ; i++)
967 attname = atts.getName(i);
968 if (attname == string("index"))
969 taxiNode.setIndex(atoi(atts.getValue(i)));
970 if (attname == string("lat"))
971 taxiNode.setLatitude(atts.getValue(i));
972 if (attname == string("lon"))
973 taxiNode.setLongitude(atts.getValue(i));
975 groundNetwork.addNode(taxiNode);
977 if (name == string("arc"))
979 taxiSegment.setIndex(++index);
980 for (int i = 0; i < atts.size() ; i++)
982 attname = atts.getName(i);
983 if (attname == string("begin"))
984 taxiSegment.setStartNodeRef(atoi(atts.getValue(i)));
985 if (attname == string("end"))
986 taxiSegment.setEndNodeRef(atoi(atts.getValue(i)));
988 groundNetwork.addSegment(taxiSegment);
990 // sort by radius, in asending order, so that smaller gates are first in the list
993 void FGAirport::endElement (const char * name) {
994 //cout << "End element " << name << endl;
998 void FGAirport::data (const char * s, int len) {
999 string token = string(s,len);
1000 //cout << "Character data " << string(s,len) << endl;
1001 //if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos))
1004 //value = string("");
1007 void FGAirport::pi (const char * target, const char * data) {
1008 //cout << "Processing instruction " << target << ' ' << data << endl;
1011 void FGAirport::warning (const char * message, int line, int column) {
1012 cout << "Warning: " << message << " (" << line << ',' << column << ')'
1016 void FGAirport::error (const char * message, int line, int column) {
1017 cout << "Error: " << message << " (" << line << ',' << column << ')'
1021 void FGAirport::setRwyUse(FGRunwayPreference& ref)
1024 //cerr << "Exiting due to not implemented yet" << endl;
1027 void FGAirport::getActiveRunway(string trafficType, int action, string *runway)
1036 if (!(rwyPrefs.available()))
1038 chooseRunwayFallback(runway);
1039 return; // generic fall back goes here
1043 RunwayGroup *currRunwayGroup = 0;
1044 int nrActiveRunways = 0;
1045 time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
1046 if (((dayStart - lastUpdate) > 600) || trafficType != prevTrafficType)
1050 //lastUpdate = dayStart;
1051 prevTrafficType = trafficType;
1054 stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
1055 ->getEnvironment(getLatitude(),
1059 windSpeed = stationweather.get_wind_speed_kt();
1060 windHeading = stationweather.get_wind_from_heading_deg();
1061 double averageWindSpeed = 0;
1062 double averageWindHeading = 0;
1063 double cosHeading = 0;
1064 double sinHeading = 0;
1065 // Initialize at the beginning of the next day or startup
1066 if ((lastUpdate == 0) || (dayStart < lastUpdate))
1068 for (int i = 0; i < 10; i++)
1070 avWindHeading [i] = windHeading;
1071 avWindSpeed [i] = windSpeed;
1076 if (windSpeed != avWindSpeed[9]) // update if new metar data
1078 // shift the running average
1079 for (int i = 0; i < 9 ; i++)
1081 avWindHeading[i] = avWindHeading[i+1];
1082 avWindSpeed [i] = avWindSpeed [i+1];
1085 avWindHeading[9] = windHeading;
1086 avWindSpeed [9] = windSpeed;
1089 for (int i = 0; i < 10; i++)
1091 averageWindSpeed += avWindSpeed [i];
1092 //averageWindHeading += avWindHeading [i];
1093 cosHeading += cos(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
1094 sinHeading += sin(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
1096 averageWindSpeed /= 10;
1097 //averageWindHeading /= 10;
1100 averageWindHeading = atan2(sinHeading, cosHeading) *SG_RADIANS_TO_DEGREES;
1101 if (averageWindHeading < 0)
1102 averageWindHeading += 360.0;
1103 //cerr << "Wind Heading " << windHeading << " average " << averageWindHeading << endl;
1104 //cerr << "Wind Speed " << windSpeed << " average " << averageWindSpeed << endl;
1105 lastUpdate = dayStart;
1106 //if (wind_speed == 0) {
1107 // wind_heading = 270; This forces West-facing rwys to be used in no-wind situations
1108 // which is consistent with Flightgear's initial setup.
1111 //string rwy_no = globals->get_runways()->search(apt->getId(), int(wind_heading));
1112 string scheduleName;
1113 //cerr << "finding active Runway for" << _id << endl;
1114 //cerr << "Nr of seconds since day start << " << dayStart << endl;
1115 ScheduleTime *currSched;
1116 //cerr << "A"<< endl;
1117 currSched = rwyPrefs.getSchedule(trafficType.c_str());
1120 //cerr << "B"<< endl;
1121 scheduleName = currSched->getName(dayStart);
1122 maxTail = currSched->getTailWind ();
1123 maxCross = currSched->getCrossWind ();
1124 //cerr << "SChedule anme = " << scheduleName << endl;
1125 if (scheduleName.empty())
1127 //cerr << "C"<< endl;
1128 currRunwayGroup = rwyPrefs.getGroup(scheduleName);
1129 //cerr << "D"<< endl;
1130 if (!(currRunwayGroup))
1132 nrActiveRunways = currRunwayGroup->getNrActiveRunways();
1133 //cerr << "Nr of Active Runways = " << nrActiveRunways << endl;
1134 currRunwayGroup->setActive(_id, averageWindSpeed, averageWindHeading, maxTail, maxCross);
1135 nrActiveRunways = currRunwayGroup->getNrActiveRunways();
1136 for (int i = 0; i < nrActiveRunways; i++)
1138 type = "unknown"; // initialize to something other than landing or takeoff
1139 currRunwayGroup->getActive(i, &name, &type);
1140 if (type == "landing")
1142 landing.push_back(name);
1143 //cerr << "Landing " << name << endl;
1145 if (type == "takeoff")
1147 takeoff.push_back(name);
1148 //cerr << "takeoff " << name << endl;
1152 if (action == 1) // takeoff
1154 int nr = takeoff.size();
1157 *runway = takeoff[(rand() % nr)];
1161 chooseRunwayFallback(runway);
1164 if (action == 2) // landing
1166 int nr = landing.size();
1169 *runway = landing[(rand() % nr)];
1173 chooseRunwayFallback(runway);
1177 //*runway = globals->get_runways()->search(_id, int(windHeading));
1178 //cerr << "Seleceted runway: " << *runway << endl;
1182 void FGAirport::chooseRunwayFallback(string *runway)
1185 stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
1186 ->getEnvironment(getLatitude(),
1190 double windSpeed = stationweather.get_wind_speed_kt();
1191 double windHeading = stationweather.get_wind_from_heading_deg();
1192 if (windSpeed == 0) {
1193 windHeading = 270; // This forces West-facing rwys to be used in no-wind situations
1194 //which is consistent with Flightgear's initial setup.
1197 *runway = globals->get_runways()->search(_id, int(windHeading));
1198 return; // generic fall back goes here
1203 /**************************************************************************
1205 *************************************************************************/
1206 FGTaxiNode::FGTaxiNode()
1210 /***************************************************************************
1212 **************************************************************************/
1213 FGTaxiSegment::FGTaxiSegment()
1217 void FGTaxiSegment::setStart(FGTaxiNodeVector *nodes)
1219 FGTaxiNodeVectorIterator i = nodes->begin();
1220 while (i != nodes->end())
1222 if (i->getIndex() == startNode)
1224 start = i->getAddress();
1225 i->addSegment(this);
1232 void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
1234 FGTaxiNodeVectorIterator i = nodes->begin();
1235 while (i != nodes->end())
1237 if (i->getIndex() == endNode)
1239 end = i->getAddress();
1246 // There is probably a computationally cheaper way of
1248 void FGTaxiSegment::setTrackDistance()
1251 SGWayPoint first (start->getLongitude(),
1252 start->getLatitude(),
1254 SGWayPoint second (end->getLongitude(),
1257 first.CourseAndDistance(second, &course, &length);
1261 bool FGTaxiRoute::next(int *val)
1263 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
1264 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
1265 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
1266 //if (currNode != nodes.end())
1267 // cerr << "true" << endl;
1269 // cerr << "false" << endl;
1271 if (currNode == nodes.end())
1277 /***************************************************************************
1279 **************************************************************************/
1281 FGGroundNetwork::FGGroundNetwork()
1286 void FGGroundNetwork::addSegment(FGTaxiSegment seg)
1288 segments.push_back(seg);
1291 void FGGroundNetwork::addNode(FGTaxiNode node)
1293 nodes.push_back(node);
1296 void FGGroundNetwork::addNodes(FGParkingVec *parkings)
1299 FGParkingVecIterator i = parkings->begin();
1300 while (i != parkings->end())
1302 n.setIndex(i->getIndex());
1303 n.setLatitude(i->getLatitude());
1304 n.setLongitude(i->getLongitude());
1313 void FGGroundNetwork::init()
1316 FGTaxiSegmentVectorIterator i = segments.begin();
1317 while(i != segments.end()) {
1318 //cerr << "initializing node " << i->getIndex() << endl;
1319 i->setStart(&nodes);
1321 i->setTrackDistance();
1322 //cerr << "Track distance = " << i->getLength() << endl;
1323 //cerr << "Track ends at" << i->getEnd()->getIndex() << endl;
1329 int FGGroundNetwork::findNearestNode(double lat, double lon)
1331 double minDist = HUGE;
1332 double course, dist;
1334 SGWayPoint first (lon,
1338 for (FGTaxiNodeVectorIterator
1339 itr = nodes.begin();
1340 itr != nodes.end(); itr++)
1343 SGWayPoint second (itr->getLongitude(),
1346 first.CourseAndDistance(second, &course, &dist);
1350 index = itr->getIndex();
1351 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
1357 FGTaxiNode *FGGroundNetwork::findNode(int idx)
1359 for (FGTaxiNodeVectorIterator
1360 itr = nodes.begin();
1361 itr != nodes.end(); itr++)
1363 if (itr->getIndex() == idx)
1364 return itr->getAddress();
1369 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end)
1373 FGTaxiNode *firstNode = findNode(start);
1374 FGTaxiNode *lastNode = findNode(end);
1375 //prevNode = prevPrevNode = -1;
1379 trace(firstNode, end, 0, 0);
1384 cerr << "Failed to find route from waypoint " << start << " to " << end << endl;
1387 sort(routes.begin(), routes.end());
1388 //for (intVecIterator i = route.begin(); i != route.end(); i++)
1390 // rte->push_back(*i);
1393 if (routes.begin() != routes.end())
1394 return *(routes.begin());
1400 void FGGroundNetwork::trace(FGTaxiNode *currNode, int end, int depth, double distance)
1402 traceStack.push_back(currNode->getIndex());
1403 totalDistance += distance;
1404 //cerr << "Starting trace " << depth << " total distance: " << totalDistance<< endl;
1405 //<< currNode->getIndex() << endl;
1407 // If the current route matches the required end point we found a valid route
1408 // So we can add this to the routing table
1409 if (currNode->getIndex() == end)
1411 //cerr << "Found route : " << totalDistance << "" << " " << *(traceStack.end()-1) << endl;
1412 routes.push_back(FGTaxiRoute(traceStack,totalDistance));
1413 traceStack.pop_back();
1415 maxDistance = totalDistance;
1417 if (totalDistance < maxDistance)
1418 maxDistance = totalDistance;
1420 totalDistance -= distance;
1425 // search if the currentNode has been encountered before
1426 // if so, we should step back one level, because it is
1427 // rather rediculous to proceed further from here.
1428 // if the current node has not been encountered before,
1429 // i should point to traceStack.end()-1; and we can continue
1430 // if i is not traceStack.end, the previous node was found,
1431 // and we should return.
1432 // This only works at trace levels of 1 or higher though
1434 intVecIterator i = traceStack.begin();
1435 while ((*i) != currNode->getIndex()) {
1436 //cerr << "Route so far : " << (*i) << endl;
1439 if (i != traceStack.end()-1) {
1440 traceStack.pop_back();
1441 totalDistance -= distance;
1444 // If the total distance from start to the current waypoint
1445 // is longer than that of a route we can also stop this trace
1446 // and go back one level.
1447 if ((totalDistance > maxDistance) && foundRoute)
1449 //cerr << "Stopping rediculously long trace: " << totalDistance << endl;
1450 traceStack.pop_back();
1451 totalDistance -= distance;
1456 //cerr << "2" << endl;
1457 if (currNode->getBeginRoute() != currNode->getEndRoute())
1459 //cerr << "3" << endl;
1460 for (FGTaxiSegmentPointerVectorIterator
1461 i = currNode->getBeginRoute();
1462 i != currNode->getEndRoute();
1465 //cerr << (*i)->getLenght() << endl;
1466 trace((*i)->getEnd(), end, depth+1, (*i)->getLength());
1468 // // cerr << currNode -> getIndex() << " ";
1469 // route.push_back(currNode->getIndex());
1476 cerr << "4" << endl;
1478 traceStack.pop_back();
1479 totalDistance -= distance;
1485 /******************************************************************************
1487 *****************************************************************************/
1489 // Populates a list of subdirectories of $FG_ROOT/Airports/AI so that
1490 // the add() method doesn't have to try opening 2 XML files in each of
1491 // thousands of non-existent directories. FIXME: should probably add
1492 // code to free this list after parsing of apt.dat is finished;
1493 // non-issue at the moment, however, as there are no AI subdirectories
1494 // in the base package.
1495 FGAirportList::FGAirportList()
1499 SGPath aid( globals->get_fg_root() );
1500 aid.append( "/Airports/AI" );
1501 if((d = ulOpenDir(aid.c_str())) == NULL)
1503 while((dent = ulReadDir(d)) != NULL) {
1504 cerr << "Dent: " << dent->d_name; // DEBUG
1505 ai_dirs.insert(dent->d_name);
1511 FGAirportList::~FGAirportList( void ) {
1512 for(unsigned int i = 0; i < airports_array.size(); ++i) {
1513 delete airports_array[i];
1518 // add an entry to the list
1519 void FGAirportList::add( const string id, const double longitude,
1520 const double latitude, const double elevation,
1521 const string name, const bool has_metar )
1523 FGRunwayPreference rwyPrefs;
1524 FGAirport* a = new FGAirport(id, longitude, latitude, elevation, name, has_metar);
1525 SGPath parkpath( globals->get_fg_root() );
1526 parkpath.append( "/Airports/AI/" );
1527 parkpath.append(id);
1528 parkpath.append("parking.xml");
1530 SGPath rwyPrefPath( globals->get_fg_root() );
1531 rwyPrefPath.append( "/Airports/AI/" );
1532 rwyPrefPath.append(id);
1533 rwyPrefPath.append("rwyuse.xml");
1534 if (ai_dirs.find(id.c_str()) != ai_dirs.end()
1535 && parkpath.exists())
1538 readXML(parkpath.str(),*a);
1541 catch (const sg_exception &e) {
1542 //cerr << "unable to read " << parkpath.str() << endl;
1545 if (ai_dirs.find(id.c_str()) != ai_dirs.end()
1546 && rwyPrefPath.exists())
1549 readXML(rwyPrefPath.str(), rwyPrefs);
1550 a->setRwyUse(rwyPrefs);
1552 catch (const sg_exception &e) {
1553 //cerr << "unable to read " << rwyPrefPath.str() << endl;
1558 airports_by_id[a->getId()] = a;
1559 // try and read in an auxilary file
1561 airports_array.push_back( a );
1562 SG_LOG( SG_GENERAL, SG_BULK, "Adding " << id << " pos = " << longitude
1563 << ", " << latitude << " elev = " << elevation );
1567 // search for the specified id
1568 FGAirport* FGAirportList::search( const string& id) {
1569 airport_map_iterator itr = airports_by_id.find(id);
1570 return(itr == airports_by_id.end() ? NULL : itr->second);
1574 // search for first subsequent alphabetically to supplied id
1575 const FGAirport* FGAirportList::findFirstById( const string& id, bool exact ) {
1576 airport_map_iterator itr;
1578 itr = airports_by_id.find(id);
1580 itr = airports_by_id.lower_bound(id);
1582 if(itr == airports_by_id.end()) {
1585 return(itr->second);
1590 // search for the airport nearest the specified position
1591 FGAirport* FGAirportList::search( double lon_deg, double lat_deg,
1594 double min_dist = 360.0;
1596 for ( i = 0; i < airports_array.size(); ++i ) {
1597 // crude manhatten distance based on lat/lon difference
1598 double d = fabs(lon_deg - airports_array[i]->getLongitude())
1599 + fabs(lat_deg - airports_array[i]->getLatitude());
1600 if ( d < min_dist ) {
1601 if ( !with_metar || (with_metar&&airports_array[i]->getMetar()) ) {
1608 return ( closest > -1 ? airports_array[closest] : NULL );
1613 FGAirportList::size () const
1615 return airports_array.size();
1618 const FGAirport *FGAirportList::getAirport( unsigned int index ) const
1620 if(index < airports_array.size()) {
1621 return(airports_array[index]);
1629 * Mark the specified airport record as not having metar
1631 void FGAirportList::no_metar( const string &id ) {
1632 if(airports_by_id.find(id) != airports_by_id.end()) {
1633 airports_by_id[id]->setMetar(false);
1639 * Mark the specified airport record as (yes) having metar
1641 void FGAirportList::has_metar( const string &id ) {
1642 if(airports_by_id.find(id) != airports_by_id.end()) {
1643 airports_by_id[id]->setMetar(true);