]> git.mxchange.org Git - flightgear.git/blobdiff - src/Airports/dynamics.cxx
Merge branch 'next' into durk-atc
[flightgear.git] / src / Airports / dynamics.cxx
index 721abc2bc2a6008c3099cae4b4ece7d944b1fd24..3d443d08fa227456ee6a41227367bdb40dfd6633 100644 (file)
@@ -26,9 +26,6 @@
 
 #include <simgear/compiler.h>
 
-#include <plib/sg.h>
-#include <plib/ul.h>
-
 #include <Environment/environment_mgr.hxx>
 #include <Environment/environment.hxx>
 #include <simgear/misc/sg_path.hxx>
 #include <Main/globals.hxx>
 #include <Main/fg_props.hxx>
 #include <Airports/runways.hxx>
+#include <ATCDCL/ATCutils.hxx>
 
-#include STL_STRING
+#include <string>
 #include <vector>
 
-SG_USING_STD(string);
-SG_USING_STD(vector);
-SG_USING_STD(sort);
-SG_USING_STD(random_shuffle);
+using std::string;
+using std::vector;
+using std::sort;
+using std::random_shuffle;
 
+#include "simple.hxx"
 #include "dynamics.hxx"
 
-/********** FGAirport Dynamics *********************************************/
-
-FGAirportDynamics::FGAirportDynamics(double lat, double lon, double elev, string id) :
-  _longitude(lon),
-  _latitude(lat),
-  _elevation(elev),
-  _id(id)
-{
-  lastUpdate = 0;
-  for (int i = 0; i < 10; i++)
-    {
-      avWindHeading [i] = 0;
-      avWindSpeed   [i] = 0;
-    }
-}
-
-
-// Note that the ground network should also be copied
-FGAirportDynamics::FGAirportDynamics(const FGAirportDynamics& other) 
+FGAirportDynamics::FGAirportDynamics(FGAirport * ap):
+    _ap(ap), rwyPrefs(ap), SIDs(ap),
+    atisSequenceIndex(-1),
+    atisSequenceTimeStamp(0.0),
+    startupController(this)
 {
-  for (FGParkingVecConstIterator ip= other.parkings.begin(); ip != other.parkings.end(); ip++)
-    parkings.push_back(*(ip));
-  rwyPrefs = other.rwyPrefs;
-  lastUpdate = other.lastUpdate;
-  
-  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];
-    }
+    lastUpdate = 0;
 }
 
 // Destructor
 FGAirportDynamics::~FGAirportDynamics()
 {
-  
 }
 
 
 // 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();
+    // 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.setParent(_ap);
+    groundNetwork.addNodes(&parkings);
+    groundNetwork.init();
+    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)
+bool FGAirportDynamics::getAvailableParking(double *lat, double *lon,
+                                            double *heading, int *gateId,
+                                            double rad,
+                                            const string & flType,
+                                            const string & acType,
+                                            const string & airline)
 {
-  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;
+    bool found = false;
+    bool available = false;
+
+
+    FGParkingVecIterator i;
+    if (parkings.begin() == parkings.end()) {
+        //cerr << "Could not find parking spot at " << _ap->getId() << endl;
+        *lat = _ap->getLatitude();
+        *lon = _ap->getLongitude();
+        *heading = 0;
+        found = true;
+    } else {
+        // First try finding a parking with a designated airline code
+        for (i = parkings.begin(); !(i == parkings.end() || found); i++) {
+            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
+                //cerr << "Code = " << airline << ": Codes " << i->getCodes();
+                if (i->getCodes().find(airline, 0) == string::npos) {
+                    available = false;
+                    //cerr << "Unavailable" << endl;
+                    continue;
+                } else {
+                    //cerr << "Available" << endl;
+                }
+            }
+            // 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;
+            }
+        }
     }
-  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;
-           }
-       }
+    if (!found) {
+        //cerr << "Traffic overflow at" << _ap->getId() 
+        //           << ". flType = " << flType 
+        //           << ". airline = " << airline 
+        //           << " Radius = " <<rad
+        //           << endl;
+        *lat = _ap->getLatitude();
+        *lon = _ap->getLongitude();
+        *heading = 0;
+        *gateId = -1;
+        //exit(1);
     }
-  if (!found)
-    {
-      //cerr << "Traffic overflow at" << _id 
-      //          << ". flType = " << flType 
-      //          << ". airline = " << airline 
-      //          << " Radius = " <<rad
-      //          << endl;
-      *lat = _latitude;
-      *lon = _longitude;
-      *heading = 0;
-      *gateId  = -1;
-      //exit(1);
-    }
-  return found;
+    return found;
 }
 
-void FGAirportDynamics::getParking (int id, double *lat, double* lon, double *heading)
+void FGAirportDynamics::getParking(int id, double *lat, double *lon,
+                                   double *heading)
 {
-  if (id < 0)
-    {
-      *lat = _latitude;
-      *lon = _longitude;
-      *heading = 0;
+    if (id < 0) {
+        *lat = _ap->getLatitude();
+        *lon = _ap->getLongitude();
+        *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();
+            }
+        }
     }
-  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 
+FGParking *FGAirportDynamics::getParking(int id)
+{
+    FGParkingVecIterator i = parkings.begin();
+    for (i = parkings.begin(); i != parkings.end(); i++) {
+        if (id == i->getIndex()) {
+            return &(*i);
+        }
+    }
     return 0;
 }
-string FGAirportDynamics::getParkingName(int i) 
-{ 
-  if (i < (int)parkings.size() && i >= 0) 
-    return (parkings[i].getName()); 
-  else 
+
+string FGAirportDynamics::getParkingName(int id)
+{
+    FGParkingVecIterator i = parkings.begin();
+    for (i = parkings.begin(); i != parkings.end(); i++) {
+        if (id == i->getIndex()) {
+            return i->getName();
+        }
+    }
+
     return string("overflow");
 }
+
 void FGAirportDynamics::releaseParking(int id)
 {
-  if (id >= 0)
-    {
-      
-      FGParkingVecIterator i = parkings.begin();
-      for (i = parkings.begin(); i != parkings.end(); i++)
-       {
-         if (id == i->getIndex())
-           {
-             i -> setAvailable(true);
-           }
-       }
+    if (id >= 0) {
+
+        FGParkingVecIterator i = parkings.begin();
+        for (i = parkings.begin(); i != parkings.end(); i++) {
+            if (id == i->getIndex()) {
+                i->setAvailable(true);
+            }
+        }
     }
 }
-  
-void  FGAirportDynamics::startXML () {
-  //cout << "Start XML" << endl;
-}
 
-void  FGAirportDynamics::endXML () {
-  //cout << "End XML" << endl;
+void FGAirportDynamics::setRwyUse(const FGRunwayPreference & ref)
+{
+    rwyPrefs = ref;
+    //cerr << "Exiting due to not implemented yet" << endl;
+    //exit(1);
 }
 
-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::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;
     }
-  if (name == string("node")) 
+
+    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 (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();
+        }
+    }
+
+    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;
+        }
     }
-  // sort by radius, in asending order, so that smaller gates are first in the list
+    //cerr << "Using active runway " << runway << " for heading " << heading << endl;
+    return runway;
 }
 
-void  FGAirportDynamics::endElement (const char * name) {
-  //cout << "End element " << name << endl;
+void FGAirportDynamics::getActiveRunway(const string & trafficType,
+                                        int action, string & runway,
+                                        double heading)
+{
+    bool ok = innerGetActiveRunway(trafficType, action, runway, heading);
+    if (!ok) {
+        runway = chooseRunwayFallback();
+    }
+}
 
+string FGAirportDynamics::chooseRunwayFallback()
+{
+    FGRunway *rwy = _ap->getActiveRunwayForUsage();
+    return rwy->ident();
 }
 
-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::addParking(FGParking & park)
+{
+    parkings.push_back(park);
 }
 
-void  FGAirportDynamics::pi (const char * target, const char * data) {
-  //cout << "Processing instruction " << target << ' ' << data << endl;
+double FGAirportDynamics::getLatitude() const
+{
+    return _ap->getLatitude();
 }
 
-void  FGAirportDynamics::warning (const char * message, int line, int column) {
-  SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
+double FGAirportDynamics::getLongitude() const
+{
+    return _ap->getLongitude();
 }
 
-void  FGAirportDynamics::error (const char * message, int line, int column) {
-  SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')');
+double FGAirportDynamics::getElevation() const
+{
+    return _ap->getElevation();
 }
 
-void FGAirportDynamics::setRwyUse(const FGRunwayPreference& ref)
+const string & FGAirportDynamics::getId() const
 {
-  rwyPrefs = ref;
-  //cerr << "Exiting due to not implemented yet" << endl;
-  //exit(1);
+    return _ap->getId();
 }
-void FGAirportDynamics::getActiveRunway(const string &trafficType, int action, string &runway)
+
+// 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)
 {
-  double windSpeed;
-  double windHeading;
-  double maxTail;
-  double maxCross;
-  string name;
-  string type;
-
-  if (!(rwyPrefs.available()))
-    {
-      runway = chooseRunwayFallback();
-      return; // generic fall back goes here
+    //return freqGround.size() ? freqGround[0] : 0; };
+    int groundFreq = 0;
+    if (leg < 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 (((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, 
-                                    &currentlyActive); 
-
-         // 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 (freqGround.size() == 0) {
+        return 0;
     }
+    if ((freqGround.size() > leg - 1) && (leg > 1)) {
+        groundFreq = freqGround[leg - 1];
+    }
+    if ((freqGround.size() < leg - 1) && (leg > 1)) {
+        groundFreq =
+            (freqGround.size() <
+             (leg - 1)) ? freqGround[freqGround.size() -
+                                     1] : freqGround[leg - 2];
+    }
+    if ((freqGround.size() >= leg - 1) && (leg > 1)) {
+        groundFreq = freqGround[leg - 2];
+    }
+    return groundFreq;
 }
 
-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));
+int FGAirportDynamics::getTowerFrequency(unsigned nr)
+{
+    int towerFreq = 0;
+    if (nr < 2) {
+        SG_LOG(SG_ATC, SG_ALERT,
+               "Leg value is smaller than two at " << SG_ORIGIN);
+    }
+    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;
+}
+
+
+FGAIFlightPlan *FGAirportDynamics::getSID(string activeRunway,
+                                          double heading)
+{
+    return SIDs.getBest(activeRunway, heading);
+}
+
+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));
 }