#endif
#include <algorithm>
+#include <string>
+#include <vector>
-#include <simgear/compiler.h>
+#include <boost/foreach.hpp>
-#include <plib/sg.h>
-#include <plib/ul.h>
+#include <simgear/compiler.h>
#include <Environment/environment_mgr.hxx>
#include <Environment/environment.hxx>
#include <simgear/props/props.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/debug/logstream.hxx>
-#include <simgear/route/waypoint.hxx>
#include <Main/globals.hxx>
#include <Main/fg_props.hxx>
#include <Airports/runways.hxx>
+#include <ATCDCL/ATCutils.hxx>
+#include <Navaids/NavDataCache.hxx>
-#include STL_STRING
-#include <vector>
-
-SG_USING_STD(string);
-SG_USING_STD(vector);
-SG_USING_STD(sort);
-SG_USING_STD(random_shuffle);
-
+#include "airport.hxx"
#include "dynamics.hxx"
-/********** FGAirport Dynamics *********************************************/
+using std::string;
+using std::vector;
+using std::sort;
+using std::random_shuffle;
-FGAirportDynamics::FGAirportDynamics(double lat, double lon, double elev, string id) :
- _longitude(lon),
- _latitude(lat),
- _elevation(elev),
- _id(id)
+class ParkingAssignment::ParkingAssignmentPrivate
{
- lastUpdate = 0;
- for (int i = 0; i < 10; i++)
- {
- avWindHeading [i] = 0;
- avWindSpeed [i] = 0;
+public:
+ ParkingAssignmentPrivate(FGParking* pk, FGAirport* apt) :
+ refCount(0),
+ parking(pk),
+ airport(apt)
+ {
+ assert(pk);
+ assert(apt);
+ retain(); // initial count of 1
+ }
+
+ ~ParkingAssignmentPrivate()
+ {
+ airport->getDynamics()->releaseParking(parking->guid());
+ }
+
+ void release()
+ {
+ if ((--refCount) == 0) {
+ delete this;
}
+ }
+
+ void retain()
+ {
+ ++refCount;
+ }
+
+ unsigned int refCount;
+ SGSharedPtr<FGParking> parking;
+ SGSharedPtr<FGAirport> airport;
+};
+
+ParkingAssignment::ParkingAssignment() :
+ _sharedData(NULL)
+{
+}
+
+ParkingAssignment::~ParkingAssignment()
+{
+ if (_sharedData) {
+ _sharedData->release();
+ }
+}
+
+ParkingAssignment::ParkingAssignment(FGParking* pk, FGAirport* apt) :
+ _sharedData(NULL)
+{
+ if (pk) {
+ _sharedData = new ParkingAssignmentPrivate(pk, apt);
+ }
}
+ParkingAssignment::ParkingAssignment(const ParkingAssignment& aOther) :
+ _sharedData(aOther._sharedData)
+{
+ if (_sharedData) {
+ _sharedData->retain();
+ }
+}
-// Note that the ground network should also be copied
-FGAirportDynamics::FGAirportDynamics(const FGAirportDynamics& other)
+void ParkingAssignment::operator=(const ParkingAssignment& aOther)
{
- for (FGParkingVecConstIterator ip= other.parkings.begin(); ip != other.parkings.end(); ip++)
- parkings.push_back(*(ip));
- rwyPrefs = other.rwyPrefs;
- lastUpdate = other.lastUpdate;
+ if (_sharedData == aOther._sharedData) {
+ return; // self-assignment, special case
+ }
- stringVecConstIterator il;
- for (il = other.landing.begin(); il != other.landing.end(); il++)
- landing.push_back(*il);
- for (il = other.takeoff.begin(); il != other.takeoff.end(); il++)
- takeoff.push_back(*il);
- lastUpdate = other.lastUpdate;
- for (int i = 0; i < 10; i++)
- {
- avWindHeading [i] = other.avWindHeading[i];
- avWindSpeed [i] = other.avWindSpeed [i];
- }
+ if (_sharedData) {
+ _sharedData->release();
+ }
+
+ _sharedData = aOther._sharedData;
+ if (_sharedData) {
+ _sharedData->retain();
+ }
+}
+
+void ParkingAssignment::release()
+{
+ if (_sharedData) {
+ _sharedData->release();
+ _sharedData = NULL;
+ }
+}
+
+bool ParkingAssignment::isValid() const
+{
+ return (_sharedData != NULL);
+}
+
+FGParking* ParkingAssignment::parking() const
+{
+ return _sharedData ? _sharedData->parking.ptr() : NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+FGAirportDynamics::FGAirportDynamics(FGAirport * ap):
+ _ap(ap), rwyPrefs(ap),
+ startupController (this),
+ towerController (this),
+ approachController (this),
+ atisSequenceIndex(-1),
+ atisSequenceTimeStamp(0.0)
+
+{
+ lastUpdate = 0;
}
// Destructor
// Initialization required after XMLRead
-void FGAirportDynamics::init()
+void FGAirportDynamics::init()
{
- // This may seem a bit weird to first randomly shuffle the parkings
- // and then sort them again. However, parkings are sorted here by ascending
- // radius. Since many parkings have similar radii, with each radius class they will
- // still be allocated relatively systematically. Randomizing prior to sorting will
- // prevent any initial orderings to be destroyed, leading (hopefully) to a more
- // naturalistic gate assignment.
- random_shuffle(parkings.begin(), parkings.end());
- sort(parkings.begin(), parkings.end());
- // add the gate positions to the ground network.
- groundNetwork.addNodes(&parkings);
- groundNetwork.init();
- groundNetwork .setTowerController(&towerController);
+ groundNetwork.init(_ap);
+ groundNetwork.setTowerController(&towerController);
+
}
-bool FGAirportDynamics::getAvailableParking(double *lat, double *lon, double *heading, int *gateId, double rad, const string &flType, const string &acType, const string &airline)
+FGParking* FGAirportDynamics::innerGetAvailableParking(double radius, const string & flType,
+ const string & airline,
+ bool skipEmptyAirlineCode)
{
- bool found = false;
- bool available = false;
- //string gateType;
-
- FGParkingVecIterator i;
-// if (flType == "cargo")
-// {
-// gateType = "RAMP_CARGO";
-// }
-// else if (flType == "ga")
-// {
-// gateType = "RAMP_GA";
-// }
-// else gateType = "GATE";
-
- if (parkings.begin() == parkings.end())
- {
- //cerr << "Could not find parking spot at " << _id << endl;
- *lat = _latitude;
- *lon = _longitude;
- *heading = 0;
- found = true;
+ flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
+ BOOST_FOREACH(PositionedID pk, cache->findAirportParking(_ap->guid(), flType, radius)) {
+ if (!isParkingAvailable(pk)) {
+ continue;
}
- else
- {
- // First try finding a parking with a designated airline code
- for (i = parkings.begin(); !(i == parkings.end() || found); i++)
- {
- //cerr << "Gate Id: " << i->getIndex()
- // << " Type : " << i->getType()
- // << " Codes : " << i->getCodes()
- // << " Radius: " << i->getRadius()
- // << " Name : " << i->getName()
- // << " Available: " << i->isAvailable() << endl;
- available = true;
- // Taken by another aircraft
- if (!(i->isAvailable()))
- {
- available = false;
- continue;
- }
- // No airline codes, so skip
- if (i->getCodes().empty())
- {
- available = false;
- continue;
- }
- else // Airline code doesn't match
- if (i->getCodes().find(airline, 0) == string::npos)
- {
- available = false;
- continue;
- }
- // Type doesn't match
- if (i->getType() != flType)
- {
- available = false;
- continue;
- }
- // too small
- if (i->getRadius() < rad)
- {
- available = false;
- continue;
- }
-
- if (available)
- {
- *lat = i->getLatitude ();
- *lon = i->getLongitude();
- *heading = i->getHeading ();
- *gateId = i->getIndex ();
- i->setAvailable(false);
- found = true;
- }
- }
- // then try again for those without codes.
- for (i = parkings.begin(); !(i == parkings.end() || found); i++)
- {
- available = true;
- if (!(i->isAvailable()))
- {
- available = false;
- continue;
- }
- if (!(i->getCodes().empty()))
- {
- if ((i->getCodes().find(airline,0) == string::npos))
- {
- available = false;
- continue;
- }
- }
- if (i->getType() != flType)
- {
- available = false;
- continue;
- }
-
- if (i->getRadius() < rad)
- {
- available = false;
- continue;
- }
-
- if (available)
- {
- *lat = i->getLatitude ();
- *lon = i->getLongitude();
- *heading = i->getHeading ();
- *gateId = i->getIndex ();
- i->setAvailable(false);
- found = true;
- }
- }
- // And finally once more if that didn't work. Now ignore the airline codes, as a last resort
- for (i = parkings.begin(); !(i == parkings.end() || found); i++)
- {
- available = true;
- if (!(i->isAvailable()))
- {
- available = false;
- continue;
- }
- if (i->getType() != flType)
- {
- available = false;
- continue;
- }
-
- if (i->getRadius() < rad)
- {
- available = false;
- continue;
- }
-
- if (available)
- {
- *lat = i->getLatitude ();
- *lon = i->getLongitude();
- *heading = i->getHeading ();
- *gateId = i->getIndex ();
- i->setAvailable(false);
- found = true;
- }
- }
+
+ FGParking* parking = getParking(pk);
+ if (skipEmptyAirlineCode && parking->getCodes().empty()) {
+ continue;
}
- if (!found)
- {
- //cerr << "Traffic overflow at" << _id
- // << ". flType = " << flType
- // << ". airline = " << airline
- // << " Radius = " <<rad
- // << endl;
- *lat = _latitude;
- *lon = _longitude;
- *heading = 0;
- *gateId = -1;
- //exit(1);
+
+ if (!airline.empty() && !parking->getCodes().empty()) {
+ if (parking->getCodes().find(airline, 0) == string::npos) {
+ continue;
+ }
}
- return found;
+
+ setParkingAvailable(pk, false);
+ return parking;
+ }
+
+ return NULL;
}
-void FGAirportDynamics::getParking (int id, double *lat, double* lon, double *heading)
+ParkingAssignment FGAirportDynamics::getAvailableParking(double radius, const string & flType,
+ const string & acType,
+ const string & airline)
{
- if (id < 0)
- {
- *lat = _latitude;
- *lon = _longitude;
- *heading = 0;
- }
- else
- {
- FGParkingVecIterator i = parkings.begin();
- for (i = parkings.begin(); i != parkings.end(); i++)
- {
- if (id == i->getIndex())
- {
- *lat = i->getLatitude();
- *lon = i->getLongitude();
- *heading = i->getHeading();
- }
- }
- }
-}
-
-FGParking *FGAirportDynamics::getParking(int i)
-{
- if (i < (int)parkings.size())
- return &(parkings[i]);
- else
- return 0;
+ SG_UNUSED(acType); // sadly not used at the moment
+
+ // most exact seach - airline codes must be present and match
+ FGParking* result = innerGetAvailableParking(radius, flType, airline, true);
+ if (result) {
+ return ParkingAssignment(result, _ap);
+ }
+
+ // more tolerant - gates with empty airline codes are permitted
+ result = innerGetAvailableParking(radius, flType, airline, false);
+ if (result) {
+ return ParkingAssignment(result, _ap);
+ }
+
+ // fallback - ignore the airline code entirely
+ result = innerGetAvailableParking(radius, flType, string(), false);
+ return result ? ParkingAssignment(result, _ap) : ParkingAssignment();
}
-string FGAirportDynamics::getParkingName(int i)
-{
- if (i < (int)parkings.size() && i >= 0)
- return (parkings[i].getName());
- else
- return string("overflow");
+
+FGParking *FGAirportDynamics::getParking(PositionedID id) const
+{
+ return static_cast<FGParking*>(flightgear::NavDataCache::instance()->loadById(id));
}
-void FGAirportDynamics::releaseParking(int id)
+
+string FGAirportDynamics::getParkingName(PositionedID id) const
{
- if (id >= 0)
- {
-
- FGParkingVecIterator i = parkings.begin();
- for (i = parkings.begin(); i != parkings.end(); i++)
- {
- if (id == i->getIndex())
- {
- i -> setAvailable(true);
- }
- }
- }
+ FGParking* p = getParking(id);
+ if (p) {
+ return p->getName();
+ }
+
+ return string();
}
+
+ParkingAssignment FGAirportDynamics::getParkingByName(const std::string& name) const
+{
+ PositionedID guid = flightgear::NavDataCache::instance()->airportItemWithIdent(parent()->guid(), FGPositioned::PARKING, name);
+ if (guid == 0) {
+ return ParkingAssignment();
+ }
-void FGAirportDynamics::startXML () {
- //cout << "Start XML" << endl;
+ return ParkingAssignment(getParking(guid), _ap);
}
-void FGAirportDynamics::endXML () {
- //cout << "End XML" << endl;
+void FGAirportDynamics::setParkingAvailable(PositionedID guid, bool available)
+{
+ if (available) {
+ releaseParking(guid);
+ } else {
+ occupiedParkings.insert(guid);
+ }
}
-void FGAirportDynamics::startElement (const char * name, const XMLAttributes &atts) {
- // const char *attval;
- FGParking park;
- FGTaxiNode taxiNode;
- FGTaxiSegment taxiSegment;
- int index = 0;
- taxiSegment.setIndex(index);
- //cout << "Start element " << name << endl;
- string attname;
- string value;
- string gateName;
- string gateNumber;
- string lat;
- string lon;
- if (name == string("Parking"))
- {
- for (int i = 0; i < atts.size(); i++)
- {
- //cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
- attname = atts.getName(i);
- if (attname == string("index"))
- park.setIndex(atoi(atts.getValue(i)));
- else if (attname == string("type"))
- park.setType(atts.getValue(i));
- else if (attname == string("name"))
- gateName = atts.getValue(i);
- else if (attname == string("number"))
- gateNumber = atts.getValue(i);
- else if (attname == string("lat"))
- park.setLatitude(atts.getValue(i));
- else if (attname == string("lon"))
- park.setLongitude(atts.getValue(i));
- else if (attname == string("heading"))
- park.setHeading(atof(atts.getValue(i)));
- else if (attname == string("radius")) {
- string radius = atts.getValue(i);
- if (radius.find("M") != string::npos)
- radius = radius.substr(0, radius.find("M",0));
- //cerr << "Radius " << radius <<endl;
- park.setRadius(atof(radius.c_str()));
- }
- else if (attname == string("airlineCodes"))
- park.setCodes(atts.getValue(i));
- }
- park.setName((gateName+gateNumber));
- parkings.push_back(park);
+bool FGAirportDynamics::isParkingAvailable(PositionedID parking) const
+{
+ return (occupiedParkings.find(parking) == occupiedParkings.end());
+}
+
+void FGAirportDynamics::releaseParking(PositionedID id)
+{
+ ParkingSet::iterator it = occupiedParkings.find(id);
+ if (it == occupiedParkings.end()) {
+ return;
+ }
+
+ occupiedParkings.erase(it);
+}
+
+void FGAirportDynamics::setRwyUse(const FGRunwayPreference & ref)
+{
+ rwyPrefs = ref;
+}
+
+bool FGAirportDynamics::innerGetActiveRunway(const string & trafficType,
+ int action, string & runway,
+ double heading)
+{
+ double windSpeed;
+ double windHeading;
+ double maxTail;
+ double maxCross;
+ string name;
+ string type;
+
+ if (!rwyPrefs.available()) {
+ return false;
+ }
+
+ RunwayGroup *currRunwayGroup = 0;
+ int nrActiveRunways = 0;
+ time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
+ if ((abs((long) (dayStart - lastUpdate)) > 600)
+ || trafficType != prevTrafficType) {
+ landing.clear();
+ takeoff.clear();
+ lastUpdate = dayStart;
+ prevTrafficType = trafficType;
+ /*
+ FGEnvironment
+ stationweather =
+ ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
+ ->getEnvironment(getLatitude(), getLongitude(),
+ getElevation());
+ */
+ windSpeed = fgGetInt("/environment/metar/base-wind-speed-kt"); //stationweather.get_wind_speed_kt();
+ windHeading = fgGetInt("/environment/metar/base-wind-dir-deg");
+ //stationweather.get_wind_from_heading_deg();
+ string scheduleName;
+ //cerr << "finding active Runway for : " << _ap->getId() << endl;
+ //cerr << "Wind Heading : " << windHeading << endl;
+ //cerr << "Wind Speed : " << windSpeed << endl;
+
+ //cerr << "Nr of seconds since day start << " << dayStart << endl;
+
+ ScheduleTime *currSched;
+ //cerr << "A"<< endl;
+ currSched = rwyPrefs.getSchedule(trafficType.c_str());
+ if (!(currSched))
+ return false;
+ //cerr << "B"<< endl;
+ scheduleName = currSched->getName(dayStart);
+ maxTail = currSched->getTailWind();
+ maxCross = currSched->getCrossWind();
+ //cerr << "Current Schedule = : " << scheduleName << endl;
+ if (scheduleName.empty())
+ return false;
+ //cerr << "C"<< endl;
+ currRunwayGroup = rwyPrefs.getGroup(scheduleName);
+ //cerr << "D"<< endl;
+ if (!(currRunwayGroup))
+ return false;
+ nrActiveRunways = currRunwayGroup->getNrActiveRunways();
+
+ // Keep a history of the currently active runways, to ensure
+ // that an already established selection of runways will not
+ // be overridden once a more preferred selection becomes
+ // available as that can lead to random runway swapping.
+ if (trafficType == "com") {
+ currentlyActive = &comActive;
+ } else if (trafficType == "gen") {
+ currentlyActive = &genActive;
+ } else if (trafficType == "mil") {
+ currentlyActive = &milActive;
+ } else if (trafficType == "ul") {
+ currentlyActive = &ulActive;
+ }
+
+ //cerr << "Durrently active selection for " << trafficType << ": ";
+ for (stringVecIterator it = currentlyActive->begin();
+ it != currentlyActive->end(); it++) {
+ //cerr << (*it) << " ";
+ }
+ //cerr << endl;
+
+ currRunwayGroup->setActive(_ap,
+ windSpeed,
+ windHeading,
+ maxTail, maxCross, currentlyActive);
+
+ // Note that I SHOULD keep multiple lists in memory, one for
+ // general aviation, one for commercial and one for military
+ // traffic.
+ currentlyActive->clear();
+ nrActiveRunways = currRunwayGroup->getNrActiveRunways();
+ //cerr << "Choosing runway for " << trafficType << endl;
+ for (int i = 0; i < nrActiveRunways; i++) {
+ type = "unknown"; // initialize to something other than landing or takeoff
+ currRunwayGroup->getActive(i, name, type);
+ 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;
+ }
+ }
+ //cerr << endl;
}
- if (name == string("node"))
+
+ if (action == 1) // takeoff
{
- for (int i = 0; i < atts.size() ; i++)
- {
- attname = atts.getName(i);
- if (attname == string("index"))
- taxiNode.setIndex(atoi(atts.getValue(i)));
- if (attname == string("lat"))
- taxiNode.setLatitude(atts.getValue(i));
- if (attname == string("lon"))
- taxiNode.setLongitude(atts.getValue(i));
- }
- groundNetwork.addNode(taxiNode);
+ 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 = chooseRwyByHeading(takeoff, heading);
+ } else { // Fallback
+ runway = chooseRunwayFallback();
+ }
}
- if (name == string("arc"))
+
+ if (action == 2) // landing
{
- taxiSegment.setIndex(++index);
- for (int i = 0; i < atts.size() ; i++)
- {
- attname = atts.getName(i);
- if (attname == string("begin"))
- taxiSegment.setStartNodeRef(atoi(atts.getValue(i)));
- if (attname == string("end"))
- taxiSegment.setEndNodeRef(atoi(atts.getValue(i)));
- }
- groundNetwork.addSegment(taxiSegment);
+ int nr = landing.size();
+ if (nr) {
+ runway = chooseRwyByHeading(landing, heading);
+ } else { //fallback
+ runway = chooseRunwayFallback();
+ }
}
- // sort by radius, in asending order, so that smaller gates are first in the list
-}
-void FGAirportDynamics::endElement (const char * name) {
- //cout << "End element " << name << endl;
+ return true;
+}
+string FGAirportDynamics::chooseRwyByHeading(stringVec rwys,
+ double heading)
+{
+ double bestError = 360.0;
+ double rwyHeading, headingError;
+ string runway;
+ for (stringVecIterator i = rwys.begin(); i != rwys.end(); i++) {
+ if (!_ap->hasRunwayWithIdent(*i)) {
+ SG_LOG(SG_ATC, SG_WARN, "chooseRwyByHeading: runway " << *i <<
+ " not found at " << _ap->ident());
+ continue;
+ }
+
+ FGRunway *rwy = _ap->getRunwayByIdent((*i));
+ rwyHeading = rwy->headingDeg();
+ headingError = fabs(heading - rwyHeading);
+ if (headingError > 180)
+ headingError = fabs(headingError - 360);
+ if (headingError < bestError) {
+ runway = (*i);
+ bestError = headingError;
+ }
+ }
+ //cerr << "Using active runway " << runway << " for heading " << heading << endl;
+ return runway;
}
-void FGAirportDynamics::data (const char * s, int len) {
- string token = string(s,len);
- //cout << "Character data " << string(s,len) << endl;
- //if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos))
- //value += token;
- //else
- //value = string("");
+void FGAirportDynamics::getActiveRunway(const string & trafficType,
+ int action, string & runway,
+ double heading)
+{
+ bool ok = innerGetActiveRunway(trafficType, action, runway, heading);
+ if (!ok) {
+ runway = chooseRunwayFallback();
+ }
}
-void FGAirportDynamics::pi (const char * target, const char * data) {
- //cout << "Processing instruction " << target << ' ' << data << endl;
+string FGAirportDynamics::chooseRunwayFallback()
+{
+ FGRunway *rwy = _ap->getActiveRunwayForUsage();
+ return rwy->ident();
}
-void FGAirportDynamics::warning (const char * message, int line, int column) {
- SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
+double FGAirportDynamics::getElevation() const
+{
+ return _ap->getElevation();
}
-void FGAirportDynamics::error (const char * message, int line, int column) {
- SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')');
+const string FGAirportDynamics::getId() const
+{
+ return _ap->getId();
}
-void FGAirportDynamics::setRwyUse(const FGRunwayPreference& ref)
+// Experimental: Return a different ground frequency depending on the leg of the
+// Flight. Leg should always have a minimum value of two when this function is called.
+// Note that in this scheme, the assignment of various frequencies to various ground
+// operations is completely arbitrary. As such, is a short cut I need to take now,
+// so that at least I can start working on assigning different frequencies to different
+// operations.
+
+int FGAirportDynamics::getGroundFrequency(unsigned leg)
{
- rwyPrefs = ref;
- //cerr << "Exiting due to not implemented yet" << endl;
- //exit(1);
+ //return freqGround.size() ? freqGround[0] : 0; };
+ //cerr << "Getting frequency for : " << leg << endl;
+ int groundFreq = 0;
+ if (leg < 1) {
+ SG_LOG(SG_ATC, SG_ALERT,
+ "Leg value is smaller than one at " << SG_ORIGIN);
+ }
+ if (freqGround.size() == 0) {
+ return 0;
+ }
+
+ if ((freqGround.size() < leg) && (leg > 0)) {
+ groundFreq =
+ (freqGround.size() <=
+ (leg - 1)) ? freqGround[freqGround.size() -
+ 1] : freqGround[leg - 1];
+ }
+ if ((freqGround.size() >= leg) && (leg > 0)) {
+ groundFreq = freqGround[leg - 1];
+ }
+ return groundFreq;
}
-void FGAirportDynamics::getActiveRunway(const string &trafficType, int action, string &runway)
+
+int FGAirportDynamics::getTowerFrequency(unsigned nr)
{
- double windSpeed;
- double windHeading;
- double maxTail;
- double maxCross;
- string name;
- string type;
-
- if (!(rwyPrefs.available()))
- {
- runway = chooseRunwayFallback();
- return; // generic fall back goes here
+ int towerFreq = 0;
+ if (nr < 2) {
+ SG_LOG(SG_ATC, SG_ALERT,
+ "Leg value is smaller than two at " << SG_ORIGIN);
}
- else
- {
- RunwayGroup *currRunwayGroup = 0;
- int nrActiveRunways = 0;
- time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
- if ((abs((long)(dayStart - lastUpdate)) > 600) || trafficType != prevTrafficType)
- {
- landing.clear();
- takeoff.clear();
- lastUpdate = dayStart;
- prevTrafficType = trafficType;
-
- FGEnvironment
- stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
- ->getEnvironment(getLatitude(),
- getLongitude(),
- getElevation());
-
- 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;
-// }
-
-// 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());
- if (!(currSched))
- return;
- //cerr << "B"<< endl;
- scheduleName = currSched->getName(dayStart);
- maxTail = currSched->getTailWind ();
- maxCross = currSched->getCrossWind ();
- //cerr << "SChedule anme = " << scheduleName << endl;
- if (scheduleName.empty())
- return;
- //cerr << "C"<< endl;
- currRunwayGroup = rwyPrefs.getGroup(scheduleName);
- //cerr << "D"<< endl;
- if (!(currRunwayGroup))
- return;
- nrActiveRunways = currRunwayGroup->getNrActiveRunways();
- //cerr << "Nr of Active Runways = " << nrActiveRunways << endl;
-
- //
- 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++)
- {
- type = "unknown"; // initialize to something other than landing or takeoff
- currRunwayGroup->getActive(i, name, type);
- 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;
- }
- }
- }
- if (action == 1) // takeoff
- {
- 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
- { // Fallback
- runway = chooseRunwayFallback();
- }
- }
- if (action == 2) // landing
- {
- int nr = landing.size();
- if (nr)
- {
- runway = landing[(rand() % nr)];
- }
- else
- { //fallback
- runway = chooseRunwayFallback();
- }
- }
-
- //runway = globals->get_runways()->search(_id, int(windHeading));
- //cerr << "Seleceted runway: " << runway << endl;
+ if (freqTower.size() == 0) {
+ return 0;
+ }
+ if ((freqTower.size() > nr - 1) && (nr > 1)) {
+ towerFreq = freqTower[nr - 1];
+ }
+ if ((freqTower.size() < nr - 1) && (nr > 1)) {
+ towerFreq =
+ (freqTower.size() <
+ (nr - 1)) ? freqTower[freqTower.size() -
+ 1] : freqTower[nr - 2];
}
+ if ((freqTower.size() >= nr - 1) && (nr > 1)) {
+ towerFreq = freqTower[nr - 2];
+ }
+ return towerFreq;
}
-string FGAirportDynamics::chooseRunwayFallback()
-{
- FGEnvironment
- stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
- ->getEnvironment(getLatitude(),
- getLongitude(),
- getElevation());
-
- double windSpeed = stationweather.get_wind_speed_kt();
- double windHeading = stationweather.get_wind_from_heading_deg();
- if (windSpeed == 0) {
- windHeading = 270; // This forces West-facing rwys to be used in no-wind situations
- //which is consistent with Flightgear's initial setup.
- }
-
- return globals->get_runways()->search(_id, int(windHeading));
+const std::string FGAirportDynamics::getAtisSequence()
+{
+ if (atisSequenceIndex == -1) {
+ updateAtisSequence(1, false);
+ }
+
+ return GetPhoneticLetter(atisSequenceIndex);
+}
+
+int FGAirportDynamics::updateAtisSequence(int interval, bool forceUpdate)
+{
+ double now = globals->get_sim_time_sec();
+ if (atisSequenceIndex == -1) {
+ // first computation
+ atisSequenceTimeStamp = now;
+ atisSequenceIndex = rand() % LTRS; // random initial sequence letters
+ return atisSequenceIndex;
+ }
+
+ int steps = static_cast<int>((now - atisSequenceTimeStamp) / interval);
+ atisSequenceTimeStamp += (interval * steps);
+ if (forceUpdate && (steps == 0)) {
+ ++steps; // a "special" ATIS update is required
+ }
+
+ atisSequenceIndex = (atisSequenceIndex + steps) % LTRS;
+ // return a huge value if no update occurred
+ return (atisSequenceIndex + (steps ? 0 : LTRS*1000));
}