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>
41 #include <Environment/environment_mgr.hxx>
42 #include <Environment/environment.hxx>
43 #include <simgear/misc/sg_path.hxx>
44 #include <simgear/props/props.hxx>
45 #include <simgear/structure/subsystem_mgr.hxx>
46 #include <simgear/debug/logstream.hxx>
47 #include <Main/globals.hxx>
48 #include <Main/fg_props.hxx>
49 #include <Airports/runways.hxx>
57 /******************************************************************************
59 ***************e*************************************************************/
60 void ScheduleTime::clear()
64 scheduleNames.clear();
68 ScheduleTime::ScheduleTime(const ScheduleTime &other)
71 timeVecConstIterator i;
72 for (i = other.start.begin(); i != other.start.end(); i++)
74 for (i = other.end.begin(); i != other.end.end(); i++)
76 stringVecConstIterator k;
77 for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); k++)
78 scheduleNames.push_back(*k);
81 //stringVec scheduleNames;
82 tailWind = other.tailWind;
83 crssWind = other.tailWind;
87 ScheduleTime & ScheduleTime::operator= (const ScheduleTime &other)
91 timeVecConstIterator i;
92 for (i = other.start.begin(); i != other.start.end(); i++)
94 for (i = other.end.begin(); i != other.end.end(); i++)
96 stringVecConstIterator k;
97 for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); k++)
98 scheduleNames.push_back(*k);
101 //stringVec scheduleNames;
102 tailWind = other.tailWind;
103 crssWind = other.tailWind;
106 string ScheduleTime::getName(time_t dayStart)
108 if ((start.size() != end.size()) || (start.size() != scheduleNames.size()))
110 cerr << "Unable to parse schedule times" << endl;
115 int nrItems = start.size();
116 //cerr << "Nr of items to process: " << nrItems << endl;
119 for (unsigned int i = 0; i < start.size(); i++)
122 if ((dayStart >= start[i]) && (dayStart <= end[i]))
123 return scheduleNames[i];
126 //couldn't find one so return 0;
127 //cerr << "Returning 0 " << endl;
131 /******************************************************************************
133 *****************************************************************************/
135 RunwayList::RunwayList(const RunwayList &other)
138 stringVecConstIterator i;
139 for (i = other.preferredRunways.begin(); i != other.preferredRunways.end(); i++)
140 preferredRunways.push_back(*i);
142 RunwayList& RunwayList::operator= (const RunwayList &other)
145 preferredRunways.clear();
146 stringVecConstIterator i;
147 for (i = other.preferredRunways.begin(); i != other.preferredRunways.end(); i++)
148 preferredRunways.push_back(*i);
151 void RunwayList::set(string tp, string lst)
153 //weekday = atoi(timeCopy.substr(0,1).c_str());
154 // timeOffsetInDays = weekday - currTimeDate->getGmt()->tm_wday;
155 // timeCopy = timeCopy.substr(2,timeCopy.length());
159 while (rwys.find(",") != string::npos)
161 rwy = rwys.substr(0, rwys.find(",",0));
162 //cerr << "adding runway [" << rwy << "] to the list " << endl;
163 preferredRunways.push_back(rwy);
164 rwys.erase(0, rwys.find(",",0)+1); // erase until after the first whitspace
165 while (rwys[0] == ' ')
166 rwys.erase(0, 1); // Erase any leading whitespaces.
167 //cerr << "Remaining runway list " << rwys;
169 preferredRunways.push_back(rwys);
173 void RunwayList::clear()
176 preferredRunways.clear();
178 /****************************************************************************
180 ***************************************************************************/
182 RunwayGroup::RunwayGroup(const RunwayGroup &other)
185 RunwayListVecConstIterator i;
186 for (i = other.rwyList.begin(); i != other.rwyList.end(); i++)
187 rwyList.push_back(*i);
188 choice[0] = other.choice[0];
189 choice[1] = other.choice[1];
190 nrActive = other.nrActive;
192 RunwayGroup& RunwayGroup:: operator= (const RunwayGroup &other)
196 RunwayListVecConstIterator i;
197 for (i = other.rwyList.begin(); i != other.rwyList.end(); i++)
198 rwyList.push_back(*i);
199 choice[0] = other.choice[0];
200 choice[1] = other.choice[1];
201 nrActive = other.nrActive;
205 void RunwayGroup::setActive(string aptId,
213 int activeRwys = rwyList.size(); // get the number of runways active
215 // bool found = true;
224 nrOfPreferences = rwyList[0].getRwyList()->size();
225 for (int i = 0; i < nrOfPreferences; i++)
227 bool validSelection = true;
228 for (int j = 0; j < activeRwys; j++)
230 //cerr << "I J " << i << " " << j << endl;
231 name = rwyList[j].getRwyList(i);
232 //cerr << "Name of Runway: " << name << endl;
233 if (globals->get_runways()->search( aptId,
237 //cerr << "Succes" << endl;
238 hdgDiff = fabs(windHeading - rwy._heading);
239 //cerr << "Wind Heading: " << windHeading << "Runway Heading: " <<rwy._heading << endl;
240 //cerr << "Wind Speed : " << windSpeed << endl;
242 hdgDiff = 360 - hdgDiff;
243 //cerr << "Heading diff: " << hdgDiff << endl;
244 hdgDiff *= ((2*M_PI)/360.0); // convert to radians
245 crossWind = windSpeed * sin(hdgDiff);
246 tailWind = -windSpeed * cos(hdgDiff);
247 //cerr << "Tailwind : " << tailWind << endl;
248 //cerr << "Crosswnd : " << crossWind << endl;
249 if ((tailWind > maxTail) || (crossWind > maxCross))
250 validSelection = false;
252 cerr << "Failed to find runway " << name << " at " << aptId << endl;
259 //cerr << "Valid runay selection : " << i << endl;
260 nrActive = activeRwys;
265 // If this didn't work, due to heavy winds, try again
266 // but select only one landing and one takeoff runway.
269 for (int i = activeRwys-1; i; i--)
271 if (rwyList[i].getType() == string("landing"))
273 if (rwyList[i].getType() == string("takeoff"))
276 //cerr << "Choosing " << choice[0] << " for landing and " << choice[1] << "for takeoff" << endl;
277 nrOfPreferences = rwyList[0].getRwyList()->size();
278 for (int i = 0; i < nrOfPreferences; i++)
280 bool validSelection = true;
281 for (int j = 0; j < 2; j++)
283 //cerr << "I J " << i << " " << j << endl;
284 name = rwyList[choice[j]].getRwyList(i);
285 //cerr << "Name of Runway: " << name << endl;
286 if (globals->get_runways()->search( aptId,
290 //cerr << "Succes" << endl;
291 hdgDiff = fabs(windHeading - rwy._heading);
292 //cerr << "Wind Heading: " << windHeading << "Runway Heading: " <<rwy._heading << endl;
293 //cerr << "Wind Speed : " << windSpeed << endl;
295 hdgDiff = 360 - hdgDiff;
296 //cerr << "Heading diff: " << hdgDiff << endl;
297 hdgDiff *= ((2*M_PI)/360.0); // convert to radians
298 crossWind = windSpeed * sin(hdgDiff);
299 tailWind = -windSpeed * cos(hdgDiff);
300 //cerr << "Tailwind : " << tailWind << endl;
301 //cerr << "Crosswnd : " << crossWind << endl;
302 if ((tailWind > maxTail) || (crossWind > maxCross))
303 validSelection = false;
305 cerr << "Failed to find runway " << name << " at " << aptId << endl;
312 //cerr << "Valid runay selection : " << i << endl;
320 //RunwayListVectorIterator i; // = rwlist.begin();
321 //stringVecIterator j;
322 //for (i = rwyList.begin(); i != rwyList.end(); i++)
324 // cerr << i->getType();
325 // for (j = i->getRwyList()->begin(); j != i->getRwyList()->end(); j++)
335 void RunwayGroup::getActive(int i, string *name, string *type)
341 if (nrActive == (int)rwyList.size())
343 *name = rwyList[i].getRwyList(active);
344 *type = rwyList[i].getType();
348 *name = rwyList[choice[i]].getRwyList(active);
349 *type = rwyList[choice[i]].getType();
352 /*****************************************************************************
353 * FGRunway preference
354 ****************************************************************************/
355 FGRunwayPreference::FGRunwayPreference()
357 //cerr << "Running default Constructor" << endl;
361 FGRunwayPreference::FGRunwayPreference(const FGRunwayPreference &other)
363 initialized = other.initialized;
365 scheduleName = other.scheduleName;
367 comTimes = other.comTimes; // Commercial Traffic;
368 genTimes = other.genTimes; // General Aviation;
369 milTimes = other.milTimes; // Military Traffic;
370 currTimes= other.currTimes; // Needed for parsing;
372 rwyList = other.rwyList;
373 rwyGroup = other.rwyGroup;
374 PreferenceListConstIterator i;
375 for (i = other.preferences.begin(); i != other.preferences.end(); i++)
376 preferences.push_back(*i);
379 FGRunwayPreference & FGRunwayPreference::operator= (const FGRunwayPreference &other)
381 initialized = other.initialized;
383 scheduleName = other.scheduleName;
385 comTimes = other.comTimes; // Commercial Traffic;
386 genTimes = other.genTimes; // General Aviation;
387 milTimes = other.milTimes; // Military Traffic;
388 currTimes= other.currTimes; // Needed for parsing;
390 rwyList = other.rwyList;
391 rwyGroup = other.rwyGroup;
392 PreferenceListConstIterator i;
394 for (i = other.preferences.begin(); i != other.preferences.end(); i++)
395 preferences.push_back(*i);
399 ScheduleTime *FGRunwayPreference::getSchedule(const char *trafficType)
401 if (!(strcmp(trafficType, "com"))) {
404 if (!(strcmp(trafficType, "gen"))) {
407 if (!(strcmp(trafficType, "mil"))) {
413 RunwayGroup *FGRunwayPreference::getGroup(const string groupName)
415 PreferenceListIterator i = preferences.begin();
416 if (preferences.begin() == preferences.end())
418 while (!(i == preferences.end() || i->getName() == groupName))
420 if (i != preferences.end())
426 void FGRunwayPreference::startXML () {
427 // cout << "Start XML" << endl;
430 void FGRunwayPreference::endXML () {
431 cout << "End XML" << endl;
434 void FGRunwayPreference::startElement (const char * name, const XMLAttributes &atts) {
435 //cout << "StartElement " << name << endl;
437 if (!(strcmp(name, "wind"))) {
438 //cerr << "Will be processing Wind" << endl;
439 for (int i = 0; i < atts.size(); i++)
441 //cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
442 //attname = atts.getName(i);
443 if (atts.getName(i) == string("tail")) {
444 //cerr << "Tail Wind = " << atts.getValue(i) << endl;
445 currTimes.setTailWind(atof(atts.getValue(i)));
447 if (atts.getName(i) == string("cross")) {
448 //cerr << "Cross Wind = " << atts.getValue(i) << endl;
449 currTimes.setCrossWind(atof(atts.getValue(i)));
453 if (!(strcmp(name, "time"))) {
454 //cerr << "Will be processing time" << endl;
455 for (int i = 0; i < atts.size(); i++)
457 if (atts.getName(i) == string("start")) {
458 //cerr << "Start Time = " << atts.getValue(i) << endl;
459 currTimes.addStartTime(processTime(atts.getValue(i)));
461 if (atts.getName(i) == string("end")) {
462 //cerr << "End time = " << atts.getValue(i) << endl;
463 currTimes.addEndTime(processTime(atts.getValue(i)));
465 if (atts.getName(i) == string("schedule")) {
466 //cerr << "Schedule Name = " << atts.getValue(i) << endl;
467 currTimes.addScheduleName(atts.getValue(i));
471 if (!(strcmp(name, "takeoff"))) {
474 if (!(strcmp(name, "landing")))
478 if (!(strcmp(name, "schedule"))) {
479 for (int i = 0; i < atts.size(); i++)
481 //cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
482 //attname = atts.getName(i);
483 if (atts.getName(i) == string("name")) {
484 //cerr << "Schedule name = " << atts.getValue(i) << endl;
485 scheduleName = atts.getValue(i);
491 //based on a string containing hour and minute, return nr seconds since day start.
492 time_t FGRunwayPreference::processTime(string tme)
494 string hour = tme.substr(0, tme.find(":",0));
495 string minute = tme.substr(tme.find(":",0)+1, tme.length());
497 //cerr << "hour = " << hour << " Minute = " << minute << endl;
498 return (atoi(hour.c_str()) * 3600 + atoi(minute.c_str()) * 60);
501 void FGRunwayPreference::endElement (const char * name) {
502 //cout << "End element " << name << endl;
503 if (!(strcmp(name, "rwyuse"))) {
506 if (!(strcmp(name, "com"))) { // Commercial Traffic
507 //cerr << "Setting time table for commerical traffic" << endl;
508 comTimes = currTimes;
511 if (!(strcmp(name, "gen"))) { // General Aviation
512 //cerr << "Setting time table for general aviation" << endl;
513 genTimes = currTimes;
516 if (!(strcmp(name, "mil"))) { // Military Traffic
517 //cerr << "Setting time table for military traffic" << endl;
518 genTimes = currTimes;
522 if (!(strcmp(name, "takeoff"))) {
523 //cerr << "Adding takeoff: " << value << endl;
524 rwyList.set(name, value);
525 rwyGroup.add(rwyList);
527 if (!(strcmp(name, "landing"))) {
528 //cerr << "Adding landing: " << value << endl;
529 rwyList.set(name, value);
530 rwyGroup.add(rwyList);
532 if (!(strcmp(name, "schedule"))) {
533 //cerr << "Adding schedule" << scheduleName << endl;
534 rwyGroup.setName(scheduleName);
535 //rwyGroup.addRunways(rwyList);
536 preferences.push_back(rwyGroup);
542 void FGRunwayPreference::data (const char * s, int len) {
543 string token = string(s,len);
544 //cout << "Character data " << string(s,len) << endl;
545 //if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos))
548 // value = string("");
552 void FGRunwayPreference::pi (const char * target, const char * data) {
553 //cout << "Processing instruction " << target << ' ' << data << endl;
556 void FGRunwayPreference::warning (const char * message, int line, int column) {
557 cout << "Warning: " << message << " (" << line << ',' << column << ')'
561 void FGRunwayPreference::error (const char * message, int line, int column) {
562 cout << "Error: " << message << " (" << line << ',' << column << ')'
566 /*********************************************************************************
568 ********************************************************************************/
569 FGParking::FGParking(double lat,
584 airlineCodes = codes;
587 double FGParking::processPosition(string pos)
596 prefix= subs.substr(0,1);
597 if (prefix == string("S") || (prefix == string("W")))
599 subs = subs.substr(1, subs.length());
600 degree = subs.substr(0, subs.find(" ",0));
601 decimal = subs.substr(subs.find(" ",0), subs.length());
604 //cerr << sign << " "<< degree << " " << decimal << endl;
605 value = sign * (atof(degree.c_str()) + atof(decimal.c_str())/60.0);
606 //cerr << value <<endl;
611 /***************************************************************************
613 ***************************************************************************/
614 FGAirport::FGAirport() : _longitude(0), _latitude(0), _elevation(0)
617 for (int i = 0; i < 10; i++)
619 avWindHeading [i] = 0;
624 FGAirport::FGAirport(const FGAirport& other)
627 _longitude = other._longitude;
628 _latitude = other._latitude;
629 _elevation = other._elevation;
631 _has_metar = other._has_metar;
632 for (FGParkingVecConstIterator ip= other.parkings.begin(); ip != other.parkings.end(); ip++)
633 parkings.push_back(*(ip));
634 rwyPrefs = other.rwyPrefs;
635 lastUpdate = other.lastUpdate;
637 stringVecConstIterator il;
638 for (il = other.landing.begin(); il != other.landing.end(); il++)
639 landing.push_back(*il);
640 for (il = other.takeoff.begin(); il != other.takeoff.end(); il++)
641 takeoff.push_back(*il);
642 lastUpdate = other.lastUpdate;
643 for (int i = 0; i < 10; i++)
645 avWindHeading [i] = other.avWindHeading[i];
646 avWindSpeed [i] = other.avWindSpeed [i];
650 FGAirport::FGAirport(string id, double lon, double lat, double elev, string name, bool has_metar)
657 _has_metar = has_metar;
659 for (int i = 0; i < 10; i++)
661 avWindHeading [i] = 0;
667 bool FGAirport::getAvailableParking(double *lat, double *lon, double *heading, int *gateId, double rad, string flType, string acType, string airline)
670 bool available = false;
673 FGParkingVecIterator i;
674 // if (flType == "cargo")
676 // gateType = "RAMP_CARGO";
678 // else if (flType == "ga")
680 // gateType = "RAMP_GA";
682 // else gateType = "GATE";
684 if (parkings.begin() == parkings.end())
686 //cerr << "Could not find parking spot at " << _id << endl;
694 // First try finding a parking with a designated airline code
695 for (i = parkings.begin(); !(i == parkings.end() || found); i++)
697 //cerr << "Gate Id: " << i->getIndex()
698 // << " Type : " << i->getType()
699 // << " Codes : " << i->getCodes()
700 // << " Radius: " << i->getRadius()
701 // << " Name : " << i->getName()
702 // << " Available: " << i->isAvailable() << endl;
704 // Taken by another aircraft
705 if (!(i->isAvailable()))
710 // No airline codes, so skip
711 if (i->getCodes().empty())
716 else // Airline code doesn't match
717 if (i->getCodes().find(airline, 0) == string::npos)
722 // Type doesn't match
723 if (i->getType() != flType)
729 if (i->getRadius() < rad)
737 *lat = i->getLatitude ();
738 *lon = i->getLongitude();
739 *heading = i->getHeading ();
740 *gateId = i->getIndex ();
741 i->setAvailable(false);
745 // then try again for those without codes.
746 for (i = parkings.begin(); !(i == parkings.end() || found); i++)
749 if (!(i->isAvailable()))
754 if (!(i->getCodes().empty()))
756 if ((i->getCodes().find(airline,0) == string::npos))
762 if (i->getType() != flType)
768 if (i->getRadius() < rad)
776 *lat = i->getLatitude ();
777 *lon = i->getLongitude();
778 *heading = i->getHeading ();
779 *gateId = i->getIndex ();
780 i->setAvailable(false);
784 // And finally once more if that didn't work. Now ignore the airline codes, as a last resort
785 for (i = parkings.begin(); !(i == parkings.end() || found); i++)
788 if (!(i->isAvailable()))
793 if (i->getType() != flType)
799 if (i->getRadius() < rad)
807 *lat = i->getLatitude ();
808 *lon = i->getLongitude();
809 *heading = i->getHeading ();
810 *gateId = i->getIndex ();
811 i->setAvailable(false);
818 //cerr << "Traffic overflow at" << _id
819 // << ". flType = " << flType
820 // << ". airline = " << airline
821 // << " Radius = " <<rad
832 void FGAirport::getParking (int id, double *lat, double* lon, double *heading)
842 FGParkingVecIterator i = parkings.begin();
843 for (i = parkings.begin(); i != parkings.end(); i++)
845 if (id == i->getIndex())
847 *lat = i->getLatitude();
848 *lon = i->getLongitude();
849 *heading = i->getLongitude();
855 FGParking *FGAirport::getParking(int i)
857 if (i < (int)parkings.size())
858 return &(parkings[i]);
862 string FGAirport::getParkingName(int i)
864 if (i < (int)parkings.size() && i >= 0)
865 return (parkings[i].getName());
867 return string("overflow");
869 void FGAirport::releaseParking(int id)
874 FGParkingVecIterator i = parkings.begin();
875 for (i = parkings.begin(); i != parkings.end(); i++)
877 if (id == i->getIndex())
879 i -> setAvailable(true);
885 void FGAirport::startXML () {
886 //cout << "Start XML" << endl;
889 void FGAirport::endXML () {
890 //cout << "End XML" << endl;
893 void FGAirport::startElement (const char * name, const XMLAttributes &atts) {
894 // const char *attval;
896 //cout << "Start element " << name << endl;
903 if (name == string("Parking"))
905 for (int i = 0; i < atts.size(); i++)
907 //cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
908 attname = atts.getName(i);
909 if (attname == string("index"))
910 park.setIndex(atoi(atts.getValue(i)));
911 else if (attname == string("type"))
912 park.setType(atts.getValue(i));
913 else if (attname == string("name"))
914 gateName = atts.getValue(i);
915 else if (attname == string("number"))
916 gateNumber = atts.getValue(i);
917 else if (attname == string("lat"))
918 park.setLatitude(atts.getValue(i));
919 else if (attname == string("lon"))
920 park.setLongitude(atts.getValue(i));
921 else if (attname == string("heading"))
922 park.setHeading(atof(atts.getValue(i)));
923 else if (attname == string("radius")) {
924 string radius = atts.getValue(i);
925 if (radius.find("M") != string::npos)
926 radius = radius.substr(0, radius.find("M",0));
927 //cerr << "Radius " << radius <<endl;
928 park.setRadius(atof(radius.c_str()));
930 else if (attname == string("airlineCodes"))
931 park.setCodes(atts.getValue(i));
933 park.setName((gateName+gateNumber));
934 parkings.push_back(park);
936 // sort by radius, in asending order, so that smaller gates are first in the list
937 sort(parkings.begin(), parkings.end());
940 void FGAirport::endElement (const char * name) {
941 //cout << "End element " << name << endl;
945 void FGAirport::data (const char * s, int len) {
946 string token = string(s,len);
947 //cout << "Character data " << string(s,len) << endl;
948 //if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos))
951 //value = string("");
954 void FGAirport::pi (const char * target, const char * data) {
955 //cout << "Processing instruction " << target << ' ' << data << endl;
958 void FGAirport::warning (const char * message, int line, int column) {
959 cout << "Warning: " << message << " (" << line << ',' << column << ')'
963 void FGAirport::error (const char * message, int line, int column) {
964 cout << "Error: " << message << " (" << line << ',' << column << ')'
968 void FGAirport::setRwyUse(FGRunwayPreference& ref)
971 //cerr << "Exiting due to not implemented yet" << endl;
974 void FGAirport::getActiveRunway(string trafficType, int action, string *runway)
983 if (!(rwyPrefs.available()))
985 chooseRunwayFallback(runway);
986 return; // generic fall back goes here
990 RunwayGroup *currRunwayGroup = 0;
991 int nrActiveRunways = 0;
992 time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
993 if (((dayStart - lastUpdate) > 600) || trafficType != prevTrafficType)
997 //lastUpdate = dayStart;
998 prevTrafficType = trafficType;
1001 stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
1002 ->getEnvironment(getLatitude(),
1006 windSpeed = stationweather.get_wind_speed_kt();
1007 windHeading = stationweather.get_wind_from_heading_deg();
1008 double averageWindSpeed = 0;
1009 double averageWindHeading = 0;
1010 double cosHeading = 0;
1011 double sinHeading = 0;
1012 // Initialize at the beginning of the next day or startup
1013 if ((lastUpdate == 0) || (dayStart < lastUpdate))
1015 for (int i = 0; i < 10; i++)
1017 avWindHeading [i] = windHeading;
1018 avWindSpeed [i] = windSpeed;
1023 if (windSpeed != avWindSpeed[9]) // update if new metar data
1025 // shift the running average
1026 for (int i = 0; i < 9 ; i++)
1028 avWindHeading[i] = avWindHeading[i+1];
1029 avWindSpeed [i] = avWindSpeed [i+1];
1032 avWindHeading[9] = windHeading;
1033 avWindSpeed [9] = windSpeed;
1036 for (int i = 0; i < 10; i++)
1038 averageWindSpeed += avWindSpeed [i];
1039 //averageWindHeading += avWindHeading [i];
1040 cosHeading += cos(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
1041 sinHeading += sin(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
1043 averageWindSpeed /= 10;
1044 //averageWindHeading /= 10;
1047 averageWindHeading = atan2(sinHeading, cosHeading) *SG_RADIANS_TO_DEGREES;
1048 if (averageWindHeading < 0)
1049 averageWindHeading += 360.0;
1050 //cerr << "Wind Heading " << windHeading << " average " << averageWindHeading << endl;
1051 //cerr << "Wind Speed " << windSpeed << " average " << averageWindSpeed << endl;
1052 lastUpdate = dayStart;
1053 //if (wind_speed == 0) {
1054 // wind_heading = 270; This forces West-facing rwys to be used in no-wind situations
1055 // which is consistent with Flightgear's initial setup.
1058 //string rwy_no = globals->get_runways()->search(apt->getId(), int(wind_heading));
1059 string scheduleName;
1060 //cerr << "finding active Runway for" << _id << endl;
1061 //cerr << "Nr of seconds since day start << " << dayStart << endl;
1062 ScheduleTime *currSched;
1063 //cerr << "A"<< endl;
1064 currSched = rwyPrefs.getSchedule(trafficType.c_str());
1067 //cerr << "B"<< endl;
1068 scheduleName = currSched->getName(dayStart);
1069 maxTail = currSched->getTailWind ();
1070 maxCross = currSched->getCrossWind ();
1071 //cerr << "SChedule anme = " << scheduleName << endl;
1072 if (scheduleName.empty())
1074 //cerr << "C"<< endl;
1075 currRunwayGroup = rwyPrefs.getGroup(scheduleName);
1076 //cerr << "D"<< endl;
1077 if (!(currRunwayGroup))
1079 nrActiveRunways = currRunwayGroup->getNrActiveRunways();
1080 //cerr << "Nr of Active Runways = " << nrActiveRunways << endl;
1081 currRunwayGroup->setActive(_id, averageWindSpeed, averageWindHeading, maxTail, maxCross);
1082 nrActiveRunways = currRunwayGroup->getNrActiveRunways();
1083 for (int i = 0; i < nrActiveRunways; i++)
1085 type = "unknown"; // initialize to something other than landing or takeoff
1086 currRunwayGroup->getActive(i, &name, &type);
1087 if (type == "landing")
1089 landing.push_back(name);
1090 //cerr << "Landing " << name << endl;
1092 if (type == "takeoff")
1094 takeoff.push_back(name);
1095 //cerr << "takeoff " << name << endl;
1099 if (action == 1) // takeoff
1101 int nr = takeoff.size();
1104 *runway = takeoff[(rand() % nr)];
1108 chooseRunwayFallback(runway);
1111 if (action == 2) // landing
1113 int nr = landing.size();
1116 *runway = landing[(rand() % nr)];
1120 chooseRunwayFallback(runway);
1124 //*runway = globals->get_runways()->search(_id, int(windHeading));
1125 //cerr << "Seleceted runway: " << *runway << endl;
1129 void FGAirport::chooseRunwayFallback(string *runway)
1132 stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
1133 ->getEnvironment(getLatitude(),
1137 double windSpeed = stationweather.get_wind_speed_kt();
1138 double windHeading = stationweather.get_wind_from_heading_deg();
1139 if (windSpeed == 0) {
1140 windHeading = 270; // This forces West-facing rwys to be used in no-wind situations
1141 //which is consistent with Flightgear's initial setup.
1144 *runway = globals->get_runways()->search(_id, int(windHeading));
1145 return; // generic fall back goes here
1148 /******************************************************************************
1150 *****************************************************************************/
1152 // Populates a list of subdirectories of $FG_ROOT/Airports/AI so that
1153 // the add() method doesn't have to try opening 2 XML files in each of
1154 // thousands of non-existent directories. FIXME: should probably add
1155 // code to free this list after parsing of apt.dat is finished;
1156 // non-issue at the moment, however, as there are no AI subdirectories
1157 // in the base package.
1158 FGAirportList::FGAirportList()
1162 SGPath aid( globals->get_fg_root() );
1163 aid.append( "/Airports/AI" );
1164 if((d = ulOpenDir(aid.c_str())) == NULL)
1166 while((dent = ulReadDir(d)) != NULL) {
1167 cerr << "Dent: " << dent->d_name; // DEBUG
1168 ai_dirs.insert(dent->d_name);
1174 FGAirportList::~FGAirportList( void ) {
1175 for(unsigned int i = 0; i < airports_array.size(); ++i) {
1176 delete airports_array[i];
1181 // add an entry to the list
1182 void FGAirportList::add( const string id, const double longitude,
1183 const double latitude, const double elevation,
1184 const string name, const bool has_metar )
1186 FGRunwayPreference rwyPrefs;
1187 FGAirport* a = new FGAirport(id, longitude, latitude, elevation, name, has_metar);
1188 SGPath parkpath( globals->get_fg_root() );
1189 parkpath.append( "/Airports/AI/" );
1190 parkpath.append(id);
1191 parkpath.append("parking.xml");
1193 SGPath rwyPrefPath( globals->get_fg_root() );
1194 rwyPrefPath.append( "/Airports/AI/" );
1195 rwyPrefPath.append(id);
1196 rwyPrefPath.append("rwyuse.xml");
1197 if (ai_dirs.find(id.c_str()) != ai_dirs.end()
1198 && parkpath.exists())
1201 readXML(parkpath.str(),*a);
1203 catch (const sg_exception &e) {
1204 //cerr << "unable to read " << parkpath.str() << endl;
1207 if (ai_dirs.find(id.c_str()) != ai_dirs.end()
1208 && rwyPrefPath.exists())
1211 readXML(rwyPrefPath.str(), rwyPrefs);
1212 a->setRwyUse(rwyPrefs);
1214 catch (const sg_exception &e) {
1215 //cerr << "unable to read " << rwyPrefPath.str() << endl;
1220 airports_by_id[a->getId()] = a;
1221 // try and read in an auxilary file
1223 airports_array.push_back( a );
1224 SG_LOG( SG_GENERAL, SG_BULK, "Adding " << id << " pos = " << longitude
1225 << ", " << latitude << " elev = " << elevation );
1229 // search for the specified id
1230 FGAirport* FGAirportList::search( const string& id) {
1231 airport_map_iterator itr = airports_by_id.find(id);
1232 return(itr == airports_by_id.end() ? NULL : itr->second);
1236 // search for first subsequent alphabetically to supplied id
1237 const FGAirport* FGAirportList::findFirstById( const string& id, bool exact ) {
1238 airport_map_iterator itr;
1240 itr = airports_by_id.find(id);
1242 itr = airports_by_id.lower_bound(id);
1244 if(itr == airports_by_id.end()) {
1247 return(itr->second);
1252 // search for the airport nearest the specified position
1253 FGAirport* FGAirportList::search( double lon_deg, double lat_deg,
1256 double min_dist = 360.0;
1258 for ( i = 0; i < airports_array.size(); ++i ) {
1259 // crude manhatten distance based on lat/lon difference
1260 double d = fabs(lon_deg - airports_array[i]->getLongitude())
1261 + fabs(lat_deg - airports_array[i]->getLatitude());
1262 if ( d < min_dist ) {
1263 if ( !with_metar || (with_metar&&airports_array[i]->getMetar()) ) {
1270 return ( closest > -1 ? airports_array[closest] : NULL );
1275 FGAirportList::size () const
1277 return airports_array.size();
1280 const FGAirport *FGAirportList::getAirport( unsigned int index ) const
1282 if(index < airports_array.size()) {
1283 return(airports_array[index]);
1291 * Mark the specified airport record as not having metar
1293 void FGAirportList::no_metar( const string &id ) {
1294 if(airports_by_id.find(id) != airports_by_id.end()) {
1295 airports_by_id[id]->setMetar(false);
1301 * Mark the specified airport record as (yes) having metar
1303 void FGAirportList::has_metar( const string &id ) {
1304 if(airports_by_id.find(id) != airports_by_id.end()) {
1305 airports_by_id[id]->setMetar(true);