Improved selection of active runway (currently AI traffic only).
The original code selected the most preferable runway combination, regardless of which combination was currently active. This patch builds in some additional resistance against change, by keeping track of which runways are already in use, and forces a change in active runways only when new (wheather/time of day) conditions force it to do so, resulting in much more consistent runway assignment. behavior.
This commit is contained in:
parent
ebc9e71916
commit
b9752635a9
4 changed files with 126 additions and 84 deletions
|
@ -486,60 +486,61 @@ void FGAirportDynamics::getActiveRunway(const string &trafficType, int action, s
|
|||
|
||||
windSpeed = stationweather.get_wind_speed_kt();
|
||||
windHeading = stationweather.get_wind_from_heading_deg();
|
||||
double averageWindSpeed = 0;
|
||||
double averageWindHeading = 0;
|
||||
double cosHeading = 0;
|
||||
double sinHeading = 0;
|
||||
// Initialize at the beginning of the next day or startup
|
||||
if ((lastUpdate == 0) || (dayStart < lastUpdate))
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
avWindHeading [i] = windHeading;
|
||||
avWindSpeed [i] = windSpeed;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (windSpeed != avWindSpeed[9]) // update if new metar data
|
||||
{
|
||||
// shift the running average
|
||||
for (int i = 0; i < 9 ; i++)
|
||||
{
|
||||
avWindHeading[i] = avWindHeading[i+1];
|
||||
avWindSpeed [i] = avWindSpeed [i+1];
|
||||
}
|
||||
}
|
||||
avWindHeading[9] = windHeading;
|
||||
avWindSpeed [9] = windSpeed;
|
||||
}
|
||||
// double averageWindSpeed = 0;
|
||||
// double averageWindHeading = 0;
|
||||
// double cosHeading = 0;
|
||||
// double sinHeading = 0;
|
||||
// // Initialize at the beginning of the next day or startup
|
||||
// if ((lastUpdate == 0) || (dayStart < lastUpdate))
|
||||
// {
|
||||
// for (int i = 0; i < 10; i++)
|
||||
// {
|
||||
// avWindHeading [i] = windHeading;
|
||||
// avWindSpeed [i] = windSpeed;
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (windSpeed != avWindSpeed[9]) // update if new metar data
|
||||
// {
|
||||
// // shift the running average
|
||||
// for (int i = 0; i < 9 ; i++)
|
||||
// {
|
||||
// avWindHeading[i] = avWindHeading[i+1];
|
||||
// avWindSpeed [i] = avWindSpeed [i+1];
|
||||
// }
|
||||
// }
|
||||
// avWindHeading[9] = windHeading;
|
||||
// avWindSpeed [9] = windSpeed;
|
||||
// }
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
averageWindSpeed += avWindSpeed [i];
|
||||
//averageWindHeading += avWindHeading [i];
|
||||
cosHeading += cos(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
|
||||
sinHeading += sin(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
|
||||
}
|
||||
averageWindSpeed /= 10;
|
||||
//averageWindHeading /= 10;
|
||||
cosHeading /= 10;
|
||||
sinHeading /= 10;
|
||||
averageWindHeading = atan2(sinHeading, cosHeading) *SG_RADIANS_TO_DEGREES;
|
||||
if (averageWindHeading < 0)
|
||||
averageWindHeading += 360.0;
|
||||
//cerr << "Wind Heading " << windHeading << " average " << averageWindHeading << endl;
|
||||
//cerr << "Wind Speed " << windSpeed << " average " << averageWindSpeed << endl;
|
||||
lastUpdate = dayStart;
|
||||
//if (wind_speed == 0) {
|
||||
// wind_heading = 270; This forces West-facing rwys to be used in no-wind situations
|
||||
// which is consistent with Flightgear's initial setup.
|
||||
//}
|
||||
// for (int i = 0; i < 10; i++)
|
||||
// {
|
||||
// averageWindSpeed += avWindSpeed [i];
|
||||
// //averageWindHeading += avWindHeading [i];
|
||||
// cosHeading += cos(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
|
||||
// sinHeading += sin(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
|
||||
// }
|
||||
// averageWindSpeed /= 10;
|
||||
// //averageWindHeading /= 10;
|
||||
// cosHeading /= 10;
|
||||
// sinHeading /= 10;
|
||||
// averageWindHeading = atan2(sinHeading, cosHeading) *SG_RADIANS_TO_DEGREES;
|
||||
// if (averageWindHeading < 0)
|
||||
// averageWindHeading += 360.0;
|
||||
// //cerr << "Wind Heading " << windHeading << " average " << averageWindHeading << endl;
|
||||
// //cerr << "Wind Speed " << windSpeed << " average " << averageWindSpeed << endl;
|
||||
// lastUpdate = dayStart;
|
||||
// //if (wind_speed == 0) {
|
||||
// // wind_heading = 270; This forces West-facing rwys to be used in no-wind situations
|
||||
// // which is consistent with Flightgear's initial setup.
|
||||
// //}
|
||||
|
||||
//string rwy_no = globals->get_runways()->search(apt->getId(), int(wind_heading));
|
||||
string scheduleName;
|
||||
//cerr << "finding active Runway for" << _id << endl;
|
||||
//cerr << "Nr of seconds since day start << " << dayStart << endl;
|
||||
|
||||
ScheduleTime *currSched;
|
||||
//cerr << "A"<< endl;
|
||||
currSched = rwyPrefs.getSchedule(trafficType.c_str());
|
||||
|
@ -559,7 +560,19 @@ void FGAirportDynamics::getActiveRunway(const string &trafficType, int action, s
|
|||
return;
|
||||
nrActiveRunways = currRunwayGroup->getNrActiveRunways();
|
||||
//cerr << "Nr of Active Runways = " << nrActiveRunways << endl;
|
||||
currRunwayGroup->setActive(_id, averageWindSpeed, averageWindHeading, maxTail, maxCross);
|
||||
|
||||
//
|
||||
currRunwayGroup->setActive(_id,
|
||||
windSpeed,
|
||||
windHeading,
|
||||
maxTail,
|
||||
maxCross,
|
||||
¤tlyActive);
|
||||
|
||||
// Note that I SHOULD keep three lists in memory, one for
|
||||
// general aviation, one for commercial and one for military
|
||||
// traffic.
|
||||
currentlyActive.clear();
|
||||
nrActiveRunways = currRunwayGroup->getNrActiveRunways();
|
||||
for (int i = 0; i < nrActiveRunways; i++)
|
||||
{
|
||||
|
@ -568,11 +581,13 @@ void FGAirportDynamics::getActiveRunway(const string &trafficType, int action, s
|
|||
if (type == "landing")
|
||||
{
|
||||
landing.push_back(name);
|
||||
currentlyActive.push_back(name);
|
||||
//cerr << "Landing " << name << endl;
|
||||
}
|
||||
if (type == "takeoff")
|
||||
{
|
||||
takeoff.push_back(name);
|
||||
currentlyActive.push_back(name);
|
||||
//cerr << "takeoff " << name << endl;
|
||||
}
|
||||
}
|
||||
|
@ -582,6 +597,9 @@ void FGAirportDynamics::getActiveRunway(const string &trafficType, int action, s
|
|||
int nr = takeoff.size();
|
||||
if (nr)
|
||||
{
|
||||
// Note that the randomization below, is just a placeholder to choose between
|
||||
// multiple active runways for this action. This should be
|
||||
// under ATC control.
|
||||
runway = takeoff[(rand() % nr)];
|
||||
}
|
||||
else
|
||||
|
|
|
@ -45,6 +45,7 @@ private:
|
|||
string prevTrafficType;
|
||||
stringVec landing;
|
||||
stringVec takeoff;
|
||||
stringVec currentlyActive;
|
||||
|
||||
// Experimental keep a running average of wind dir and speed to prevent
|
||||
// Erratic runway changes.
|
||||
|
|
|
@ -197,7 +197,8 @@ void RunwayGroup::setActive(const string &aptId,
|
|||
double windSpeed,
|
||||
double windHeading,
|
||||
double maxTail,
|
||||
double maxCross)
|
||||
double maxCross,
|
||||
stringVec *currentlyActive)
|
||||
{
|
||||
|
||||
FGRunway rwy;
|
||||
|
@ -209,16 +210,31 @@ void RunwayGroup::setActive(const string &aptId,
|
|||
double crossWind;
|
||||
double tailWind;
|
||||
string name;
|
||||
//stringVec names;
|
||||
int bestMatch = 0, bestChoice = 0;
|
||||
|
||||
if (activeRwys > 0)
|
||||
{
|
||||
{
|
||||
// Now downward iterate across all the possible preferences
|
||||
// starting by the least preferred choice working toward the most preferred choice
|
||||
|
||||
nrOfPreferences = rwyList[0].getRwyList()->size();
|
||||
for (int i = 0; i < nrOfPreferences; i++)
|
||||
bool validSelection = true;
|
||||
bool foundValidSelection = false;
|
||||
for (int i = nrOfPreferences-1; i >= 0; i--)
|
||||
{
|
||||
bool validSelection = true;
|
||||
int match = 0;
|
||||
|
||||
|
||||
// Test each runway listed in the preference to see if it's possible to use
|
||||
// If one runway of the selection isn't allowed, we need to exclude this
|
||||
// preference, however, we don't want to stop right there, because we also
|
||||
// don't want to randomly swap runway preferences, unless there is a need to.
|
||||
//
|
||||
|
||||
for (int j = 0; j < activeRwys; j++)
|
||||
{
|
||||
//cerr << "I J " << i << " " << j << endl;
|
||||
validSelection = true;
|
||||
name = rwyList[j].getRwyList(i);
|
||||
//cerr << "Name of Runway: " << name << endl;
|
||||
if (globals->get_runways()->search( aptId,
|
||||
|
@ -238,20 +254,42 @@ void RunwayGroup::setActive(const string &aptId,
|
|||
//cerr << "Tailwind : " << tailWind << endl;
|
||||
//cerr << "Crosswnd : " << crossWind << endl;
|
||||
if ((tailWind > maxTail) || (crossWind > maxCross))
|
||||
validSelection = false;
|
||||
{
|
||||
//cerr << "Invalid : ";
|
||||
validSelection = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//cerr << "Valid : ";
|
||||
}
|
||||
}else {
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Failed to find runway " << name << " at " << aptId );
|
||||
exit(1);
|
||||
}
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Failed to find runway " << name << " at " << aptId );
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (validSelection)
|
||||
if (validSelection)
|
||||
{
|
||||
//cerr << "Valid runay selection : " << i << endl;
|
||||
nrActive = activeRwys;
|
||||
active = i;
|
||||
return;
|
||||
}
|
||||
//cerr << "Valid : ";
|
||||
foundValidSelection = true;
|
||||
for (stringVecIterator it = currentlyActive->begin();
|
||||
it != currentlyActive->end(); it++)
|
||||
{
|
||||
if ((*it) == name)
|
||||
match++;
|
||||
if (match >= bestMatch) {
|
||||
bestMatch = match;
|
||||
bestChoice = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
//cerr << "Preference " << i << " bestMatch " << bestMatch << " choice " << bestChoice << endl;
|
||||
}
|
||||
if (foundValidSelection)
|
||||
{
|
||||
//cerr << "Valid runay selection : " << bestChoice << endl;
|
||||
nrActive = activeRwys;
|
||||
active = bestChoice;
|
||||
return;
|
||||
}
|
||||
// If this didn't work, due to heavy winds, try again
|
||||
// but select only one landing and one takeoff runway.
|
||||
|
@ -280,16 +318,11 @@ void RunwayGroup::setActive(const string &aptId,
|
|||
{
|
||||
//cerr << "Succes" << endl;
|
||||
hdgDiff = fabs(windHeading - rwy._heading);
|
||||
//cerr << "Wind Heading: " << windHeading << "Runway Heading: " <<rwy._heading << endl;
|
||||
//cerr << "Wind Speed : " << windSpeed << endl;
|
||||
if (hdgDiff > 180)
|
||||
hdgDiff = 360 - hdgDiff;
|
||||
//cerr << "Heading diff: " << hdgDiff << endl;
|
||||
hdgDiff *= ((2*M_PI)/360.0); // convert to radians
|
||||
crossWind = windSpeed * sin(hdgDiff);
|
||||
tailWind = -windSpeed * cos(hdgDiff);
|
||||
//cerr << "Tailwind : " << tailWind << endl;
|
||||
//cerr << "Crosswnd : " << crossWind << endl;
|
||||
if ((tailWind > maxTail) || (crossWind > maxCross))
|
||||
validSelection = false;
|
||||
}else {
|
||||
|
@ -308,19 +341,7 @@ void RunwayGroup::setActive(const string &aptId,
|
|||
}
|
||||
}
|
||||
active = -1;
|
||||
//RunwayListVectorIterator i; // = rwlist.begin();
|
||||
//stringVecIterator j;
|
||||
//for (i = rwyList.begin(); i != rwyList.end(); i++)
|
||||
// {
|
||||
// cerr << i->getType();
|
||||
// for (j = i->getRwyList()->begin(); j != i->getRwyList()->end(); j++)
|
||||
// {
|
||||
// cerr << (*j);
|
||||
// }
|
||||
// cerr << endl;
|
||||
// }
|
||||
//for (int
|
||||
|
||||
nrActive = 0;
|
||||
}
|
||||
|
||||
void RunwayGroup::getActive(int i, string &name, string &type)
|
||||
|
|
|
@ -96,6 +96,7 @@ private:
|
|||
//stringVec runwayNames;
|
||||
int choice[2];
|
||||
int nrActive;
|
||||
|
||||
public:
|
||||
RunwayGroup() {};
|
||||
RunwayGroup(const RunwayGroup &other);
|
||||
|
@ -103,7 +104,7 @@ public:
|
|||
|
||||
void setName(const string& nm) { name = nm; };
|
||||
void add(const RunwayList& list) { rwyList.push_back(list);};
|
||||
void setActive(const string& aptId, double windSpeed, double windHeading, double maxTail, double maxCross);
|
||||
void setActive(const string& aptId, double windSpeed, double windHeading, double maxTail, double maxCross, stringVec *curr);
|
||||
|
||||
int getNrActiveRunways() { return nrActive;};
|
||||
void getActive(int i, string& name, string& type);
|
||||
|
@ -132,6 +133,7 @@ private:
|
|||
RunwayList rwyList;
|
||||
RunwayGroup rwyGroup;
|
||||
PreferenceList preferences;
|
||||
|
||||
|
||||
time_t processTime(const string&);
|
||||
bool initialized;
|
||||
|
|
Loading…
Add table
Reference in a new issue