]> git.mxchange.org Git - flightgear.git/blobdiff - src/Airports/dynamics.cxx
Update FGRunway to process information from threshold.xml files.
[flightgear.git] / src / Airports / dynamics.cxx
index 3f6185df20e63ef4120b76a386eac598b964c7da..3e658a1473aa5effe73f158c7fe181d1e2c797b0 100644 (file)
@@ -26,7 +26,6 @@
 
 #include <simgear/compiler.h>
 
-#include <plib/sg.h>
 #include <plib/ul.h>
 
 #include <Environment/environment_mgr.hxx>
 #include <Main/fg_props.hxx>
 #include <Airports/runways.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)
-{
+FGAirportDynamics::FGAirportDynamics(FGAirport* ap) :
+  _ap(ap), rwyPrefs(ap), SIDs(ap) {
   lastUpdate = 0;
-  for (int i = 0; i < 10; i++)
-    {
-      avWindHeading [i] = 0;
-      avWindSpeed   [i] = 0;
-    }
-}
 
+  // For testing only. This needs to be refined when we move ATIS functionality over.
+  atisInformation = "Sierra";
+}
 
 // Note that the ground network should also be copied
-FGAirportDynamics::FGAirportDynamics(const FGAirportDynamics& other) 
+FGAirportDynamics::FGAirportDynamics(const FGAirportDynamics& other) :
+  rwyPrefs(other.rwyPrefs),
+  SIDs(other.SIDs)
 {
   for (FGParkingVecConstIterator ip= other.parkings.begin(); ip != other.parkings.end(); ip++)
     parkings.push_back(*(ip));
-  rwyPrefs = other.rwyPrefs;
+  // rwyPrefs = other.rwyPrefs;
   lastUpdate = other.lastUpdate;
   
   stringVecConstIterator il;
@@ -81,11 +74,7 @@ FGAirportDynamics::FGAirportDynamics(const FGAirportDynamics& other)
   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];
-    }
+  atisInformation = other.atisInformation; 
 }
 
 // Destructor
@@ -108,7 +97,8 @@ void FGAirportDynamics::init()
   // add the gate positions to the ground network. 
   groundNetwork.addNodes(&parkings);
   groundNetwork.init();
-  groundNetwork .setTowerController(&towerController);
+  groundNetwork.setTowerController(&towerController);
+  groundNetwork.setParent(_ap);
 }
 
 bool FGAirportDynamics::getAvailableParking(double *lat, double *lon, double *heading, int *gateId, double rad, const string &flType, const string &acType, const string &airline)
@@ -130,9 +120,9 @@ bool FGAirportDynamics::getAvailableParking(double *lat, double *lon, double *he
   
   if (parkings.begin() == parkings.end())
     {
-      //cerr << "Could not find parking spot at " << _id << endl;
-      *lat = _latitude;
-      *lon = _longitude;
+      //cerr << "Could not find parking spot at " << _ap->getId() << endl;
+      *lat = _ap->getLatitude();
+      *lon = _ap->getLongitude();
       *heading = 0;
       found = true;
     }
@@ -209,10 +199,10 @@ bool FGAirportDynamics::getAvailableParking(double *lat, double *lon, double *he
          if (!(i->getCodes().empty()))
            {
              if ((i->getCodes().find(airline,0) == string::npos))
-         {
-           available = false;
-           continue;
-         }
+               {
+                   available = false;
+                   continue;
+               }
            }
          if (i->getType() != flType)
            {
@@ -235,7 +225,7 @@ bool FGAirportDynamics::getAvailableParking(double *lat, double *lon, double *he
              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++)
        {
@@ -270,13 +260,13 @@ bool FGAirportDynamics::getAvailableParking(double *lat, double *lon, double *he
     }
   if (!found)
     {
-      //cerr << "Traffic overflow at" << _id 
+      //cerr << "Traffic overflow at" << _ap->getId() 
       //          << ". flType = " << flType 
       //          << ". airline = " << airline 
       //          << " Radius = " <<rad
       //          << endl;
-      *lat = _latitude;
-      *lon = _longitude;
+      *lat = _ap->getLatitude();
+      *lon = _ap->getLongitude();
       *heading = 0;
       *gateId  = -1;
       //exit(1);
@@ -288,8 +278,8 @@ void FGAirportDynamics::getParking (int id, double *lat, double* lon, double *he
 {
   if (id < 0)
     {
-      *lat = _latitude;
-      *lon = _longitude;
+      *lat = _ap->getLatitude();
+      *lon = _ap->getLongitude();
       *heading = 0;
     }
   else
@@ -307,18 +297,27 @@ void FGAirportDynamics::getParking (int id, double *lat, double* lon, double *he
     }
 } 
 
-FGParking *FGAirportDynamics::getParking(int i) 
+FGParking *FGAirportDynamics::getParking(int id
 { 
-  if (i < (int)parkings.size()) 
-    return &(parkings[i]); 
-  else 
+    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) 
+string FGAirportDynamics::getParkingName(int id
 { 
-  if (i < (int)parkings.size() && i >= 0) 
-    return (parkings[i].getName()); 
-  else 
+    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)
@@ -337,143 +336,30 @@ void FGAirportDynamics::releaseParking(int id)
     }
 }
   
-void  FGAirportDynamics::startXML () {
-  //cout << "Start XML" << endl;
-}
-
-void  FGAirportDynamics::endXML () {
-  //cout << "End XML" << endl;
-}
-
-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);
-    }
-  if (name == string("node")) 
-    {
-      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);
-    }
-  if (name == string("arc")) 
-    {
-      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);
-    }
-  // 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;
-
-}
-
-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::pi (const char * target, const char * data) {
-  //cout << "Processing instruction " << target << ' ' << data << endl;
-}
-
-void  FGAirportDynamics::warning (const char * message, int line, int column) {
-  SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
-}
-
-void  FGAirportDynamics::error (const char * message, int line, int column) {
-  SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')');
-}
-
 void FGAirportDynamics::setRwyUse(const FGRunwayPreference& ref)
 {
   rwyPrefs = ref;
   //cerr << "Exiting due to not implemented yet" << endl;
   //exit(1);
 }
-void FGAirportDynamics::getActiveRunway(const string &trafficType, int action, string &runway)
+
+bool FGAirportDynamics::innerGetActiveRunway(const string &trafficType, int action, string &runway, double heading)
 {
-  double windSpeed;
+double windSpeed;
   double windHeading;
   double maxTail;
   double maxCross;
   string name;
   string type;
 
-  if (!(rwyPrefs.available()))
-    {
-      runway = chooseRunwayFallback();
-      return; // generic fall back goes here
-    }
-  else
-    {
-      RunwayGroup *currRunwayGroup = 0;
-      int nrActiveRunways = 0;
-      time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
-      if ((abs((long)(dayStart - lastUpdate)) > 600) || trafficType != prevTrafficType)
+  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();
@@ -488,94 +374,56 @@ 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;
-//         }
-         
-//       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 << "finding active Runway for" << _ap->getId() << endl;
          //cerr << "Nr of seconds since day start << " << dayStart << endl;
 
          ScheduleTime *currSched;
          //cerr << "A"<< endl;
          currSched = rwyPrefs.getSchedule(trafficType.c_str());
          if (!(currSched))
-           return;   
+           return false;
          //cerr << "B"<< endl;
          scheduleName = currSched->getName(dayStart);
          maxTail  = currSched->getTailWind  ();
          maxCross = currSched->getCrossWind ();
          //cerr << "SChedule anme = " << scheduleName << endl;
          if (scheduleName.empty())
-           return;
+           return false;
          //cerr << "C"<< endl;
          currRunwayGroup = rwyPrefs.getGroup(scheduleName); 
          //cerr << "D"<< endl;
          if (!(currRunwayGroup))
-           return;
+           return false;
          nrActiveRunways = currRunwayGroup->getNrActiveRunways();
-         //cerr << "Nr of Active Runways = " << nrActiveRunways << endl; 
 
+        // 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;
+        }
          // 
-         currRunwayGroup->setActive(_id
+         currRunwayGroup->setActive(_ap
                                     windSpeed, 
                                     windHeading, 
                                     maxTail, 
                                     maxCross, 
-                                    &currentlyActive); 
+                                    currentlyActive); 
 
-         // Note that I SHOULD keep three lists in memory, one for 
+         // Note that I SHOULD keep multiple lists in memory, one for 
          // general aviation, one for commercial and one for military
          // traffic.
-         currentlyActive.clear();
+         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
@@ -583,18 +431,20 @@ void FGAirportDynamics::getActiveRunway(const string &trafficType, int action, s
              if (type == "landing")
                {
                  landing.push_back(name);
-                 currentlyActive.push_back(name);
+                 currentlyActive->push_back(name);
                  //cerr << "Landing " << name << endl; 
                }
              if (type == "takeoff")
                {
                  takeoff.push_back(name);
-                 currentlyActive.push_back(name);
+                 currentlyActive->push_back(name);
                  //cerr << "takeoff " << name << endl;
                }
            }
+          //cerr << endl;
        }
-      if (action == 1) // takeoff 
+  
+  if (action == 1) // takeoff 
        {
          int nr = takeoff.size();
          if (nr)
@@ -602,45 +452,112 @@ void FGAirportDynamics::getActiveRunway(const string &trafficType, int action, s
              // 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)];
+             runway = chooseRwyByHeading (takeoff, heading);
            }
          else
            { // Fallback
              runway = chooseRunwayFallback();
            }
        } 
-      if (action == 2) // landing
+  
+  if (action == 2) // landing
        {
          int nr = landing.size();
          if (nr)
            {
-             runway = landing[(rand() % nr)];
+             runway = chooseRwyByHeading (landing, heading);
            }
          else
            {  //fallback
               runway = chooseRunwayFallback();
            }
-       }
-      
-      //runway = globals->get_runways()->search(_id, int(windHeading));
-      //cerr << "Seleceted runway: " << runway << 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++) {
+       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::getActiveRunway(const string &trafficType, int action, string &runway, double heading)
+{
+  bool ok = innerGetActiveRunway(trafficType, action, runway, heading);
+  if (!ok) {
+    runway = chooseRunwayFallback();
+  }
 }
 
 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));
+  FGRunway* rwy = _ap->getActiveRunwayForUsage();
+  return rwy->ident();
+}
+
+void FGAirportDynamics::addParking(FGParking& park) {
+  parkings.push_back(park);
+}
+
+double FGAirportDynamics::getLatitude() const {
+  return _ap->getLatitude();
+}
+
+double FGAirportDynamics::getLongitude() const {
+  return _ap->getLongitude();
+}
+
+double FGAirportDynamics::getElevation() const {
+  return _ap->getElevation();
+}
+
+const string& FGAirportDynamics::getId() const {
+  return _ap->getId();
+}
+
+// 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) { 
+     //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);
+     }
+     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;
+}
+
+FGAIFlightPlan *FGAirportDynamics::getSID(string activeRunway, double heading)
+{
+   return SIDs.getBest(activeRunway, heading);
 }