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.
33 #include <simgear/compiler.h>
34 #include <Environment/environment_mgr.hxx>
35 #include <Environment/environment.hxx>
36 #include <simgear/misc/sg_path.hxx>
37 #include <simgear/props/props.hxx>
38 #include <simgear/structure/subsystem_mgr.hxx>
39 #include <simgear/debug/logstream.hxx>
40 #include <Main/globals.hxx>
41 #include <Main/fg_props.hxx>
42 #include <Airports/runways.hxx>
48 SG_USING_NAMESPACE(std);
50 /******************************************************************************
52 ***************e*************************************************************/
53 void ScheduleTime::clear()
57 scheduleNames.clear();
61 ScheduleTime::ScheduleTime(const ScheduleTime &other)
64 timeVecConstIterator i;
65 for (i = other.start.begin(); i != other.start.end(); i++)
67 for (i = other.end.begin(); i != other.end.end(); i++)
69 stringVecConstIterator k;
70 for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); k++)
71 scheduleNames.push_back(*k);
74 //stringVec scheduleNames;
75 tailWind = other.tailWind;
76 crssWind = other.tailWind;
80 ScheduleTime & ScheduleTime::operator= (const ScheduleTime &other)
84 timeVecConstIterator i;
85 for (i = other.start.begin(); i != other.start.end(); i++)
87 for (i = other.end.begin(); i != other.end.end(); i++)
89 stringVecConstIterator k;
90 for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); k++)
91 scheduleNames.push_back(*k);
94 //stringVec scheduleNames;
95 tailWind = other.tailWind;
96 crssWind = other.tailWind;
99 string ScheduleTime::getName(time_t dayStart)
101 if ((start.size() != end.size()) || (start.size() != scheduleNames.size()))
103 cerr << "Unable to parse schedule times" << endl;
108 int nrItems = start.size();
109 //cerr << "Nr of items to process: " << nrItems << endl;
112 for (int i = 0; i < start.size(); i++)
115 if ((dayStart >= start[i]) && (dayStart <= end[i]))
116 return scheduleNames[i];
119 //couldn't find one so return 0;
120 //cerr << "Returning 0 " << endl;
124 /******************************************************************************
126 *****************************************************************************/
128 RunwayList::RunwayList(const RunwayList &other)
131 stringVecConstIterator i;
132 for (i = other.preferredRunways.begin(); i != other.preferredRunways.end(); i++)
133 preferredRunways.push_back(*i);
135 RunwayList& RunwayList::operator= (const RunwayList &other)
138 preferredRunways.clear();
139 stringVecConstIterator i;
140 for (i = other.preferredRunways.begin(); i != other.preferredRunways.end(); i++)
141 preferredRunways.push_back(*i);
144 void RunwayList::set(string tp, string lst)
146 //weekday = atoi(timeCopy.substr(0,1).c_str());
147 // timeOffsetInDays = weekday - currTimeDate->getGmt()->tm_wday;
148 // timeCopy = timeCopy.substr(2,timeCopy.length());
152 while (rwys.find(",") != string::npos)
154 rwy = rwys.substr(0, rwys.find(",",0));
155 //cerr << "adding runway [" << rwy << "] to the list " << endl;
156 preferredRunways.push_back(rwy);
157 rwys.erase(0, rwys.find(",",0)+1); // erase until after the first whitspace
158 while (rwys[0] == ' ')
159 rwys.erase(0, 1); // Erase any leading whitespaces.
160 //cerr << "Remaining runway list " << rwys;
162 preferredRunways.push_back(rwys);
166 void RunwayList::clear()
169 preferredRunways.clear();
171 /****************************************************************************
173 ***************************************************************************/
175 RunwayGroup::RunwayGroup(const RunwayGroup &other)
178 RunwayListVecConstIterator i;
179 for (i = other.rwyList.begin(); i != other.rwyList.end(); i++)
180 rwyList.push_back(*i);
181 choice[0] = other.choice[0];
182 choice[1] = other.choice[1];
183 nrActive = other.nrActive;
185 RunwayGroup& RunwayGroup:: operator= (const RunwayGroup &other)
189 RunwayListVecConstIterator i;
190 for (i = other.rwyList.begin(); i != other.rwyList.end(); i++)
191 rwyList.push_back(*i);
192 choice[0] = other.choice[0];
193 choice[1] = other.choice[1];
194 nrActive = other.nrActive;
197 void RunwayGroup::setActive(string aptId,
205 int activeRwys = rwyList.size(); // get the number of runways active
216 nrOfPreferences = rwyList[0].getRwyList()->size();
217 for (int i = 0; i < nrOfPreferences; i++)
219 bool validSelection = true;
220 for (int j = 0; j < activeRwys; j++)
222 //cerr << "I J " << i << " " << j << endl;
223 name = rwyList[j].getRwyList(i);
224 //cerr << "Name of Runway: " << name << endl;
225 if (globals->get_runways()->search( aptId,
229 //cerr << "Succes" << endl;
230 hdgDiff = fabs(windHeading - rwy._heading);
231 //cerr << "Wind Heading: " << windHeading << "Runway Heading: " <<rwy._heading << endl;
232 //cerr << "Wind Speed : " << windSpeed << endl;
234 hdgDiff = 360 - hdgDiff;
235 //cerr << "Heading diff: " << hdgDiff << endl;
236 hdgDiff *= ((2*M_PI)/360.0); // convert to radians
237 crossWind = windSpeed * sin(hdgDiff);
238 tailWind = -windSpeed * cos(hdgDiff);
239 //cerr << "Tailwind : " << tailWind << endl;
240 //cerr << "Crosswnd : " << crossWind << endl;
241 if ((tailWind > maxTail) || (crossWind > maxCross))
242 validSelection = false;
244 cerr << "Failed to find runway " << name << " at " << aptId << endl;
251 //cerr << "Valid runay selection : " << i << endl;
252 nrActive = activeRwys;
257 // If this didn't work, due to heavy winds, try again
258 // but select only one landing and one takeoff runway.
261 for (int i = activeRwys-1; i; i--)
263 if (rwyList[i].getType() == string("landing"))
265 if (rwyList[i].getType() == string("takeoff"))
268 //cerr << "Choosing " << choice[0] << " for landing and " << choice[1] << "for takeoff" << endl;
269 nrOfPreferences = rwyList[0].getRwyList()->size();
270 for (int i = 0; i < nrOfPreferences; i++)
272 bool validSelection = true;
273 for (int j = 0; j < 2; j++)
275 //cerr << "I J " << i << " " << j << endl;
276 name = rwyList[choice[j]].getRwyList(i);
277 //cerr << "Name of Runway: " << name << endl;
278 if (globals->get_runways()->search( aptId,
282 //cerr << "Succes" << endl;
283 hdgDiff = fabs(windHeading - rwy._heading);
284 //cerr << "Wind Heading: " << windHeading << "Runway Heading: " <<rwy._heading << endl;
285 //cerr << "Wind Speed : " << windSpeed << endl;
287 hdgDiff = 360 - hdgDiff;
288 //cerr << "Heading diff: " << hdgDiff << endl;
289 hdgDiff *= ((2*M_PI)/360.0); // convert to radians
290 crossWind = windSpeed * sin(hdgDiff);
291 tailWind = -windSpeed * cos(hdgDiff);
292 //cerr << "Tailwind : " << tailWind << endl;
293 //cerr << "Crosswnd : " << crossWind << endl;
294 if ((tailWind > maxTail) || (crossWind > maxCross))
295 validSelection = false;
297 cerr << "Failed to find runway " << name << " at " << aptId << endl;
304 //cerr << "Valid runay selection : " << i << endl;
312 //RunwayListVectorIterator i; // = rwlist.begin();
313 //stringVecIterator j;
314 //for (i = rwyList.begin(); i != rwyList.end(); i++)
316 // cerr << i->getType();
317 // for (j = i->getRwyList()->begin(); j != i->getRwyList()->end(); j++)
327 void RunwayGroup::getActive(int i, string *name, string *type)
333 if (nrActive == rwyList.size())
335 *name = rwyList[i].getRwyList(active);
336 *type = rwyList[i].getType();
340 *name = rwyList[choice[i]].getRwyList(active);
341 *type = rwyList[choice[i]].getType();
344 /*****************************************************************************
345 * FGRunway preference
346 ****************************************************************************/
347 FGRunwayPreference::FGRunwayPreference()
349 //cerr << "Running default Constructor" << endl;
353 FGRunwayPreference::FGRunwayPreference(const FGRunwayPreference &other)
355 initialized = other.initialized;
357 scheduleName = other.scheduleName;
359 comTimes = other.comTimes; // Commercial Traffic;
360 genTimes = other.genTimes; // General Aviation;
361 milTimes = other.milTimes; // Military Traffic;
362 currTimes= other.currTimes; // Needed for parsing;
364 rwyList = other.rwyList;
365 rwyGroup = other.rwyGroup;
366 PreferenceListConstIterator i;
367 for (i = other.preferences.begin(); i != other.preferences.end(); i++)
368 preferences.push_back(*i);
371 FGRunwayPreference & FGRunwayPreference::operator= (const FGRunwayPreference &other)
373 initialized = other.initialized;
375 scheduleName = other.scheduleName;
377 comTimes = other.comTimes; // Commercial Traffic;
378 genTimes = other.genTimes; // General Aviation;
379 milTimes = other.milTimes; // Military Traffic;
380 currTimes= other.currTimes; // Needed for parsing;
382 rwyList = other.rwyList;
383 rwyGroup = other.rwyGroup;
384 PreferenceListConstIterator i;
386 for (i = other.preferences.begin(); i != other.preferences.end(); i++)
387 preferences.push_back(*i);
391 ScheduleTime *FGRunwayPreference::getSchedule(const char *trafficType)
393 if (!(strcmp(trafficType, "com"))) {
396 if (!(strcmp(trafficType, "gen"))) {
399 if (!(strcmp(trafficType, "mil"))) {
404 RunwayGroup *FGRunwayPreference::getGroup(const string groupName)
406 PreferenceListIterator i = preferences.begin();
407 if (preferences.begin() == preferences.end())
409 while (!(i == preferences.end() || i->getName() == groupName))
411 if (i != preferences.end())
417 void FGRunwayPreference::startXML () {
418 // cout << "Start XML" << endl;
421 void FGRunwayPreference::endXML () {
422 cout << "End XML" << endl;
425 void FGRunwayPreference::startElement (const char * name, const XMLAttributes &atts) {
426 //cout << "StartElement " << name << endl;
428 if (!(strcmp(name, "wind"))) {
429 //cerr << "Will be processing Wind" << endl;
430 for (int i = 0; i < atts.size(); i++)
432 //cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
433 //attname = atts.getName(i);
434 if (atts.getName(i) == string("tail")) {
435 //cerr << "Tail Wind = " << atts.getValue(i) << endl;
436 currTimes.setTailWind(atof(atts.getValue(i)));
438 if (atts.getName(i) == string("cross")) {
439 //cerr << "Cross Wind = " << atts.getValue(i) << endl;
440 currTimes.setCrossWind(atof(atts.getValue(i)));
444 if (!(strcmp(name, "time"))) {
445 //cerr << "Will be processing time" << endl;
446 for (int i = 0; i < atts.size(); i++)
448 if (atts.getName(i) == string("start")) {
449 //cerr << "Start Time = " << atts.getValue(i) << endl;
450 currTimes.addStartTime(processTime(atts.getValue(i)));
452 if (atts.getName(i) == string("end")) {
453 //cerr << "End time = " << atts.getValue(i) << endl;
454 currTimes.addEndTime(processTime(atts.getValue(i)));
456 if (atts.getName(i) == string("schedule")) {
457 //cerr << "Schedule Name = " << atts.getValue(i) << endl;
458 currTimes.addScheduleName(atts.getValue(i));
462 if (!(strcmp(name, "takeoff"))) {
465 if (!(strcmp(name, "landing")))
469 if (!(strcmp(name, "schedule"))) {
470 for (int i = 0; i < atts.size(); i++)
472 //cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
473 //attname = atts.getName(i);
474 if (atts.getName(i) == string("name")) {
475 //cerr << "Schedule name = " << atts.getValue(i) << endl;
476 scheduleName = atts.getValue(i);
482 //based on a string containing hour and minute, return nr seconds since day start.
483 time_t FGRunwayPreference::processTime(string tme)
485 string hour = tme.substr(0, tme.find(":",0));
486 string minute = tme.substr(tme.find(":",0)+1, tme.length());
488 //cerr << "hour = " << hour << " Minute = " << minute << endl;
489 return (atoi(hour.c_str()) * 3600 + atoi(minute.c_str()) * 60);
492 void FGRunwayPreference::endElement (const char * name) {
493 //cout << "End element " << name << endl;
494 if (!(strcmp(name, "rwyuse"))) {
497 if (!(strcmp(name, "com"))) { // Commercial Traffic
498 //cerr << "Setting time table for commerical traffic" << endl;
499 comTimes = currTimes;
502 if (!(strcmp(name, "gen"))) { // General Aviation
503 //cerr << "Setting time table for general aviation" << endl;
504 genTimes = currTimes;
507 if (!(strcmp(name, "mil"))) { // Military Traffic
508 //cerr << "Setting time table for military traffic" << endl;
509 genTimes = currTimes;
513 if (!(strcmp(name, "takeoff"))) {
514 //cerr << "Adding takeoff: " << value << endl;
515 rwyList.set(name, value);
516 rwyGroup.add(rwyList);
518 if (!(strcmp(name, "landing"))) {
519 //cerr << "Adding landing: " << value << endl;
520 rwyList.set(name, value);
521 rwyGroup.add(rwyList);
523 if (!(strcmp(name, "schedule"))) {
524 //cerr << "Adding schedule" << scheduleName << endl;
525 rwyGroup.setName(scheduleName);
526 //rwyGroup.addRunways(rwyList);
527 preferences.push_back(rwyGroup);
533 void FGRunwayPreference::data (const char * s, int len) {
534 string token = string(s,len);
535 //cout << "Character data " << string(s,len) << endl;
536 //if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos))
539 // value = string("");
543 void FGRunwayPreference::pi (const char * target, const char * data) {
544 //cout << "Processing instruction " << target << ' ' << data << endl;
547 void FGRunwayPreference::warning (const char * message, int line, int column) {
548 cout << "Warning: " << message << " (" << line << ',' << column << ')'
552 void FGRunwayPreference::error (const char * message, int line, int column) {
553 cout << "Error: " << message << " (" << line << ',' << column << ')'
557 /*********************************************************************************
559 ********************************************************************************/
560 FGParking::FGParking(double lat,
575 airlineCodes = codes;
578 double FGParking::processPosition(string pos)
587 prefix= subs.substr(0,1);
588 if (prefix == string("S") || (prefix == string("W")))
590 subs = subs.substr(1, subs.length());
591 degree = subs.substr(0, subs.find(" ",0));
592 decimal = subs.substr(subs.find(" ",0), subs.length());
595 //cerr << sign << " "<< degree << " " << decimal << endl;
596 value = sign * (atof(degree.c_str()) + atof(decimal.c_str())/60.0);
597 //cerr << value <<endl;
602 /***************************************************************************
604 ***************************************************************************/
605 FGAirport::FGAirport() : _longitude(0), _latitude(0), _elevation(0)
608 for (int i = 0; i < 10; i++)
610 avWindHeading [i] = 0;
615 FGAirport::FGAirport(const FGAirport& other)
618 _longitude = other._longitude;
619 _latitude = other._latitude;
620 _elevation = other._elevation;
622 _has_metar = other._has_metar;
623 for (FGParkingVecConstIterator i= other.parkings.begin(); i != other.parkings.end(); i++)
624 parkings.push_back(*(i));
625 rwyPrefs = other.rwyPrefs;
626 lastUpdate = other.lastUpdate;
628 stringVecConstIterator i;
629 for (i = other.landing.begin(); i != other.landing.end(); i++)
630 landing.push_back(*i);
631 for (i = other.takeoff.begin(); i != other.takeoff.end(); i++)
632 takeoff.push_back(*i);
633 lastUpdate = other.lastUpdate;
634 for (int i = 0; i < 10; i++)
636 avWindHeading [i] = other.avWindHeading[i];
637 avWindSpeed [i] = other.avWindSpeed [i];
641 FGAirport::FGAirport(string id, double lon, double lat, double elev, string name, bool has_metar)
648 _has_metar = has_metar;
650 for (int i = 0; i < 10; i++)
652 avWindHeading [i] = 0;
658 bool FGAirport::getAvailableParking(double *lat, double *lon, double *heading, int *gateId, double rad, string flType, string acType, string airline)
661 bool available = false;
664 FGParkingVecIterator i;
665 // if (flType == "cargo")
667 // gateType = "RAMP_CARGO";
669 // else if (flType == "ga")
671 // gateType = "RAMP_GA";
673 // else gateType = "GATE";
675 if (parkings.begin() == parkings.end())
677 //cerr << "Could not find parking spot at " << _id << endl;
685 // First try finding a parking with a designated airline code
686 for (i = parkings.begin(); !(i == parkings.end() || found); i++)
688 //cerr << "Gate Id: " << i->getIndex()
689 // << " Type : " << i->getType()
690 // << " Codes : " << i->getCodes()
691 // << " Radius: " << i->getRadius()
692 // << " Name : " << i->getName()
693 // << " Available: " << i->isAvailable() << endl;
695 // Taken by another aircraft
696 if (!(i->isAvailable()))
701 // No airline codes, so skip
702 if (i->getCodes().empty())
707 else // Airline code doesn't match
708 if (i->getCodes().find(airline, 0) == string::npos)
713 // Type doesn't match
714 if (i->getType() != flType)
720 if (i->getRadius() < rad)
728 *lat = i->getLatitude ();
729 *lon = i->getLongitude();
730 *heading = i->getHeading ();
731 *gateId = i->getIndex ();
732 i->setAvailable(false);
736 // then try again for those without codes.
737 for (i = parkings.begin(); !(i == parkings.end() || found); i++)
740 if (!(i->isAvailable()))
745 if (!(i->getCodes().empty()))
747 if ((i->getCodes().find(airline,0) == string::npos))
753 if (i->getType() != flType)
759 if (i->getRadius() < rad)
767 *lat = i->getLatitude ();
768 *lon = i->getLongitude();
769 *heading = i->getHeading ();
770 *gateId = i->getIndex ();
771 i->setAvailable(false);
775 // And finally once more if that didn't work. Now ignore the airline codes, as a last resort
776 for (i = parkings.begin(); !(i == parkings.end() || found); i++)
779 if (!(i->isAvailable()))
784 if (i->getType() != flType)
790 if (i->getRadius() < rad)
798 *lat = i->getLatitude ();
799 *lon = i->getLongitude();
800 *heading = i->getHeading ();
801 *gateId = i->getIndex ();
802 i->setAvailable(false);
809 //cerr << "Traffic overflow at" << _id
810 // << ". flType = " << flType
811 // << ". airline = " << airline
812 // << " Radius = " <<rad
823 void FGAirport::getParking (int id, double *lat, double* lon, double *heading)
833 FGParkingVecIterator i = parkings.begin();
834 for (i = parkings.begin(); i != parkings.end(); i++)
836 if (id == i->getIndex())
838 *lat = i->getLatitude();
839 *lon = i->getLongitude();
840 *heading = i->getLongitude();
846 FGParking *FGAirport::getParking(int i)
848 if (i < parkings.size())
849 return &(parkings[i]);
853 string FGAirport::getParkingName(int i)
855 if (i < parkings.size() && i >= 0)
856 return (parkings[i].getName());
858 return string("overflow");
860 void FGAirport::releaseParking(int id)
865 FGParkingVecIterator i = parkings.begin();
866 for (i = parkings.begin(); i != parkings.end(); i++)
868 if (id == i->getIndex())
870 i -> setAvailable(true);
876 void FGAirport::startXML () {
877 //cout << "Start XML" << endl;
880 void FGAirport::endXML () {
881 //cout << "End XML" << endl;
884 void FGAirport::startElement (const char * name, const XMLAttributes &atts) {
887 //cout << "Start element " << name << endl;
894 if (name == string("Parking"))
896 for (int i = 0; i < atts.size(); i++)
898 //cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
899 attname = atts.getName(i);
900 if (attname == string("index"))
901 park.setIndex(atoi(atts.getValue(i)));
902 else if (attname == string("type"))
903 park.setType(atts.getValue(i));
904 else if (attname == string("name"))
905 gateName = atts.getValue(i);
906 else if (attname == string("number"))
907 gateNumber = atts.getValue(i);
908 else if (attname == string("lat"))
909 park.setLatitude(atts.getValue(i));
910 else if (attname == string("lon"))
911 park.setLongitude(atts.getValue(i));
912 else if (attname == string("heading"))
913 park.setHeading(atof(atts.getValue(i)));
914 else if (attname == string("radius")) {
915 string radius = atts.getValue(i);
916 if (radius.find("M") != string::npos)
917 radius = radius.substr(0, radius.find("M",0));
918 //cerr << "Radius " << radius <<endl;
919 park.setRadius(atof(radius.c_str()));
921 else if (attname == string("airlineCodes"))
922 park.setCodes(atts.getValue(i));
924 park.setName((gateName+gateNumber));
925 parkings.push_back(park);
927 // sort by radius, in asending order, so that smaller gates are first in the list
928 sort(parkings.begin(), parkings.end());
931 void FGAirport::endElement (const char * name) {
932 //cout << "End element " << name << endl;
936 void FGAirport::data (const char * s, int len) {
937 string token = string(s,len);
938 //cout << "Character data " << string(s,len) << endl;
939 //if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos))
942 //value = string("");
945 void FGAirport::pi (const char * target, const char * data) {
946 //cout << "Processing instruction " << target << ' ' << data << endl;
949 void FGAirport::warning (const char * message, int line, int column) {
950 cout << "Warning: " << message << " (" << line << ',' << column << ')'
954 void FGAirport::error (const char * message, int line, int column) {
955 cout << "Error: " << message << " (" << line << ',' << column << ')'
959 void FGAirport::setRwyUse(FGRunwayPreference& ref)
962 //cerr << "Exiting due to not implemented yet" << endl;
965 void FGAirport::getActiveRunway(string trafficType, int action, string *runway)
974 if (!(rwyPrefs.available()))
976 chooseRunwayFallback(runway);
977 return; // generic fall back goes here
981 RunwayGroup *currRunwayGroup = 0;
982 int nrActiveRunways = 0;
983 time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
984 if (((dayStart - lastUpdate) > 600) || trafficType != prevTrafficType)
988 //lastUpdate = dayStart;
989 prevTrafficType = trafficType;
992 stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
993 ->getEnvironment(getLatitude(),
997 windSpeed = stationweather.get_wind_speed_kt();
998 windHeading = stationweather.get_wind_from_heading_deg();
999 double averageWindSpeed = 0;
1000 double averageWindHeading = 0;
1001 double cosHeading = 0;
1002 double sinHeading = 0;
1003 // Initialize at the beginning of the next day or startup
1004 if ((lastUpdate == 0) || (dayStart < lastUpdate))
1006 for (int i = 0; i < 10; i++)
1008 avWindHeading [i] = windHeading;
1009 avWindSpeed [i] = windSpeed;
1014 if (windSpeed != avWindSpeed[9]) // update if new metar data
1016 // shift the running average
1017 for (int i = 0; i < 9 ; i++)
1019 avWindHeading[i] = avWindHeading[i+1];
1020 avWindSpeed [i] = avWindSpeed [i+1];
1023 avWindHeading[9] = windHeading;
1024 avWindSpeed [9] = windSpeed;
1027 for (int i = 0; i < 10; i++)
1029 averageWindSpeed += avWindSpeed [i];
1030 //averageWindHeading += avWindHeading [i];
1031 cosHeading += cos(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
1032 sinHeading += sin(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
1034 averageWindSpeed /= 10;
1035 //averageWindHeading /= 10;
1038 averageWindHeading = atan2(sinHeading, cosHeading) *SG_RADIANS_TO_DEGREES;
1039 if (averageWindHeading < 0)
1040 averageWindHeading += 360.0;
1041 //cerr << "Wind Heading " << windHeading << " average " << averageWindHeading << endl;
1042 //cerr << "Wind Speed " << windSpeed << " average " << averageWindSpeed << endl;
1043 lastUpdate = dayStart;
1044 //if (wind_speed == 0) {
1045 // wind_heading = 270; This forces West-facing rwys to be used in no-wind situations
1046 // which is consistent with Flightgear's initial setup.
1049 //string rwy_no = globals->get_runways()->search(apt->getId(), int(wind_heading));
1050 string scheduleName;
1051 //cerr << "finding active Runway for" << _id << endl;
1052 //cerr << "Nr of seconds since day start << " << dayStart << endl;
1053 ScheduleTime *currSched;
1054 //cerr << "A"<< endl;
1055 currSched = rwyPrefs.getSchedule(trafficType.c_str());
1058 //cerr << "B"<< endl;
1059 scheduleName = currSched->getName(dayStart);
1060 maxTail = currSched->getTailWind ();
1061 maxCross = currSched->getCrossWind ();
1062 //cerr << "SChedule anme = " << scheduleName << endl;
1063 if (scheduleName.empty())
1065 //cerr << "C"<< endl;
1066 currRunwayGroup = rwyPrefs.getGroup(scheduleName);
1067 //cerr << "D"<< endl;
1068 if (!(currRunwayGroup))
1070 nrActiveRunways = currRunwayGroup->getNrActiveRunways();
1071 //cerr << "Nr of Active Runways = " << nrActiveRunways << endl;
1072 currRunwayGroup->setActive(_id, averageWindSpeed, averageWindHeading, maxTail, maxCross);
1073 nrActiveRunways = currRunwayGroup->getNrActiveRunways();
1074 for (int i = 0; i < nrActiveRunways; i++)
1076 type = "unknown"; // initialize to something other than landing or takeoff
1077 currRunwayGroup->getActive(i, &name, &type);
1078 if (type == "landing")
1080 landing.push_back(name);
1081 //cerr << "Landing " << name << endl;
1083 if (type == "takeoff")
1085 takeoff.push_back(name);
1086 //cerr << "takeoff " << name << endl;
1090 if (action == 1) // takeoff
1092 int nr = takeoff.size();
1095 *runway = takeoff[(rand() % nr)];
1099 chooseRunwayFallback(runway);
1102 if (action == 2) // landing
1104 int nr = landing.size();
1107 *runway = landing[(rand() % nr)];
1111 chooseRunwayFallback(runway);
1115 //*runway = globals->get_runways()->search(_id, int(windHeading));
1116 //cerr << "Seleceted runway: " << *runway << endl;
1120 void FGAirport::chooseRunwayFallback(string *runway)
1123 stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
1124 ->getEnvironment(getLatitude(),
1128 double windSpeed = stationweather.get_wind_speed_kt();
1129 double windHeading = stationweather.get_wind_from_heading_deg();
1130 if (windSpeed == 0) {
1131 windHeading = 270; // This forces West-facing rwys to be used in no-wind situations
1132 //which is consistent with Flightgear's initial setup.
1135 *runway = globals->get_runways()->search(_id, int(windHeading));
1136 return; // generic fall back goes here
1139 /******************************************************************************
1141 *****************************************************************************/
1144 // add an entry to the list
1145 void FGAirportList::add( const string id, const double longitude,
1146 const double latitude, const double elevation,
1147 const string name, const bool has_metar )
1149 FGRunwayPreference rwyPrefs;
1150 FGAirport a(id, longitude, latitude, elevation, name, has_metar);
1152 //a._longitude = longitude;
1153 //a._latitude = latitude;
1154 //a._elevation = elevation;
1156 //a._has_metar = has_metar;
1157 SGPath parkpath( globals->get_fg_root() );
1158 parkpath.append( "/Airports/AI/" );
1159 parkpath.append(id);
1160 parkpath.append("parking.xml");
1162 SGPath rwyPrefPath( globals->get_fg_root() );
1163 rwyPrefPath.append( "/Airports/AI/" );
1164 rwyPrefPath.append(id);
1165 rwyPrefPath.append("rwyuse.xml");
1166 if (parkpath.exists())
1169 readXML(parkpath.str(),a);
1171 catch (const sg_exception &e) {
1172 //cerr << "unable to read " << parkpath.str() << endl;
1175 if (rwyPrefPath.exists())
1178 readXML(rwyPrefPath.str(), rwyPrefs);
1179 a.setRwyUse(rwyPrefs);
1181 catch (const sg_exception &e) {
1182 //cerr << "unable to read " << rwyPrefPath.str() << endl;
1187 airports_by_id[a.getId()] = a;
1188 // try and read in an auxilary file
1190 airports_array.push_back( &airports_by_id[a.getId()] );
1191 SG_LOG( SG_GENERAL, SG_BULK, "Adding " << id << " pos = " << longitude
1192 << ", " << latitude << " elev = " << elevation );
1196 // search for the specified id
1197 FGAirport FGAirportList::search( const string& id) {
1198 return airports_by_id[id];
1201 // search for the specified id and return a pointer
1202 FGAirport* FGAirportList::search( const string& id, FGAirport *result) {
1203 FGAirport* retval = airports_by_id[id].getAddress();
1204 //cerr << "Returning Airport of string " << id << " results in " << retval->getId();
1208 // search for the airport nearest the specified position
1209 FGAirport FGAirportList::search( double lon_deg, double lat_deg,
1212 double min_dist = 360.0;
1214 for ( i = 0; i < airports_array.size(); ++i ) {
1215 // crude manhatten distance based on lat/lon difference
1216 double d = fabs(lon_deg - airports_array[i]->getLongitude())
1217 + fabs(lat_deg - airports_array[i]->getLatitude());
1218 if ( d < min_dist ) {
1219 if ( !with_metar || (with_metar&&airports_array[i]->getMetar()) ) {
1226 return *airports_array[closest];
1231 FGAirportList::~FGAirportList( void ) {
1235 FGAirportList::size () const
1237 return airports_array.size();
1240 const FGAirport *FGAirportList::getAirport( int index ) const
1242 return airports_array[index];
1247 * Mark the specified airport record as not having metar
1249 void FGAirportList::no_metar( const string &id ) {
1250 airports_by_id[id].setMetar(false);
1255 * Mark the specified airport record as (yes) having metar
1257 void FGAirportList::has_metar( const string &id ) {
1258 airports_by_id[id].setMetar(true);