]> git.mxchange.org Git - flightgear.git/commitdiff
Initial attempt to establish a better integration between AI and ATC code.
authordurk <durk>
Sun, 13 Jul 2008 12:51:06 +0000 (12:51 +0000)
committerdurk <durk>
Sun, 13 Jul 2008 12:51:06 +0000 (12:51 +0000)
Various other patches that have been lingering around for a while:
 * Moved trafficcontrol.[ch]xx from the Airports directory to ATC, where
   it really belongs.
 * AI aircraft will request startup clearance, and ground control will
   approve.
 * Starting AI Aircraft will be pushed back to a predefined holding point
   on the ground network, and wait a while before taxiing out to the runway

18 files changed:
src/AIModel/AIAircraft.cxx
src/AIModel/AIAircraft.hxx
src/AIModel/AIFlightPlanCreatePushBack.cxx
src/ATC/Makefile.am [new file with mode: 0644]
src/ATC/trafficcontrol.cxx [new file with mode: 0644]
src/ATC/trafficcontrol.hxx [new file with mode: 0644]
src/ATCDCL/Makefile.am
src/Airports/Makefile.am
src/Airports/dynamicloader.cxx
src/Airports/dynamicloader.hxx
src/Airports/dynamics.cxx
src/Airports/dynamics.hxx
src/Airports/groundnetwork.cxx
src/Airports/groundnetwork.hxx
src/Airports/parking.hxx
src/Airports/runwayprefs.cxx
src/Main/Makefile.am
src/Makefile.am

index c73919d0a39dbafab322b9ca7c94b620d16df9f4..7a06ba24189a3024ea4b65653b268f3688b5f2cb 100644 (file)
@@ -150,7 +150,8 @@ void FGAIAircraft::setPerformance(const std::string& acclass) {
          return;
      }
      catch (FP_Inactive) {
-         return;
+         //return;
+         groundTargetSpeed = 0;
      }
 
      handleATCRequests(); // ATC also has a word to say
@@ -365,7 +366,7 @@ void FGAIAircraft::getGroundElev(double dt) {
     dt_elev_count += dt;
 
     // Update minimally every three secs, but add some randomness
-    // to prevent all IA objects doing this in synchrony
+    // to prevent all AI objects doing this in synchrony
     if (dt_elev_count < (3.0) + (rand() % 10))
         return;
 
@@ -413,10 +414,14 @@ void FGAIAircraft::announcePositionToController() {
     if (trafficRef) {
         int leg = fp->getLeg();
 
-        // Note that leg was been incremented after creating the current leg, so we should use
+        // Note that leg has been incremented after creating the current leg, so we should use
         // leg numbers here that are one higher than the number that is used to create the leg
         //
         switch (leg) {
+          case 2:              // Startup and Push back
+            if (trafficRef->getDepartureAirport()->getDynamics())
+                controller = trafficRef->getDepartureAirport()->getDynamics()->getStartupController();
+            break;
         case 3:              // Taxiing to runway
             if (trafficRef->getDepartureAirport()->getDynamics()->getGroundNetwork()->exists())
                 controller = trafficRef->getDepartureAirport()->getDynamics()->getGroundNetwork();
@@ -425,12 +430,14 @@ void FGAIAircraft::announcePositionToController() {
             if (trafficRef->getDepartureAirport()->getDynamics()) {
                 controller = trafficRef->getDepartureAirport()->getDynamics()->getTowerController();
                 //if (trafficRef->getDepartureAirport()->getId() == "EHAM") {
-                //cerr << trafficRef->getCallSign() << " at runway " << fp->getRunway() << "Ready for departure "
-                //   << trafficRef->getFlightType() << " to " << trafficRef->getArrivalAirport()->getId() << endl;
+                //string trns = trafficRef->getCallSign() + " at runway " + fp->getRunway() + 
+                //              ". Ready for departure. " + trafficRef->getFlightType() + " to " +
+                //              trafficRef->getArrivalAirport()->getId();
+                //fgSetString("/sim/messages/atc", trns.c_str());
                 //  if (controller == 0) {
                 //cerr << "Error in assigning controller at " << trafficRef->getDepartureAirport()->getId() << endl;
                 //}
-
+                //}
             } else {
                 cerr << "Error: Could not find Dynamics at airport : " << trafficRef->getDepartureAirport()->getId() << endl;
             }
@@ -446,29 +453,17 @@ void FGAIAircraft::announcePositionToController() {
 
         if ((controller != prevController) && (prevController != 0)) {
             prevController->signOff(getID());
-            string callsign =  trafficRef->getCallSign();
-            if ( trafficRef->getHeavy())
-                callsign += "Heavy";
-            switch (leg) {
-            case 3:
-                //cerr << callsign << " ready to taxi to runway " << fp->getRunway() << endl;
-                break;
-            case 4:
-                //cerr << callsign << " at runway " << fp->getRunway() << "Ready for take-off. "
-                //     << trafficRef->getFlightRules() << " to " << trafficRef->getArrivalAirport()->getId()
-                //     << "(" << trafficRef->getArrivalAirport()->getName() << ")."<< endl;
-                break;
-            }
         }
         prevController = controller;
         if (controller) {
             controller->announcePosition(getID(), fp, fp->getCurrentWaypoint()->routeIndex,
                                          _getLatitude(), _getLongitude(), hdg, speed, altitude_ft,
-                                         trafficRef->getRadius(), leg, trafficRef->getCallSign());
+                                         trafficRef->getRadius(), leg, this);
         }
     }
 }
 
+// Process ATC instructions and report back
 
 void FGAIAircraft::processATC(FGATCInstruction instruction) {
     if (instruction.getCheckForCircularWait()) {
@@ -494,7 +489,7 @@ void FGAIAircraft::processATC(FGATCInstruction instruction) {
     } else {
         if (holdPos) {
             //if (trafficRef)
-            // cerr << trafficRef->getCallSign() << " Resuming Taxi " << endl;
+            // cerr << trafficRef->getCallSign() << " Resuming Taxi." << endl;
             holdPos = false;
         }
         // Change speed Instruction. This can only be excecuted when there is no
@@ -760,6 +755,7 @@ void FGAIAircraft::updatePrimaryTargetValues() {
                 throw AI_OutOfSight();
             }
         }
+        timeElapsed = now - fp->getStartTime();
         if (! fp->isActive(now)) { 
             throw FP_Inactive();
         }
@@ -845,7 +841,7 @@ void FGAIAircraft::updateHeading() {
             } else {
                 bank_sense = 1.0;
             }
-            if (trafficRef)
+            //if (trafficRef)
                //cerr << trafficRef->getCallSign() << " Heading " 
                 //     << hdg << ". Target " << tgt_heading <<  ". Diff " << fabs(sum - tgt_heading) << ". Speed " << speed << endl;
             //if (headingDiff > 60) {
@@ -875,7 +871,8 @@ void FGAIAircraft::updateHeading() {
                    else
                        headingChangeRate += dt * sign(roll);
             }
-           hdg += headingChangeRate * dt;
+
+           hdg += headingChangeRate * dt * (fabs(speed) / 15);
             headingError = headingDiff;
         } else {
             if (fabs(speed) > 1.0) {
@@ -979,6 +976,20 @@ void FGAIAircraft::updatePitchAngleTarget() {
     }
 }
 
+string FGAIAircraft::atGate() {
+     string tmp("");
+     if (fp->getLeg() < 3) {
+         if (trafficRef) {
+             if (fp->getGate() > 0) {
+                 FGParking *park =
+                     trafficRef->getDepartureAirport()->getDynamics()->getParking(fp->getGate());
+                 tmp = park->getName();
+             }
+         }
+     }
+     return tmp;
+}
+
 void FGAIAircraft::handleATCRequests() {
     //TODO implement NullController for having no ATC to save the conditionals
     if (controller) {
index 1c23c2576fc13753787a300a26cb45a375229147..f4adf16ba7d18e23608589d4333cb85455b89d52 100644 (file)
@@ -70,6 +70,7 @@ public:
 
     void announcePositionToController(); //TODO have to be public?
     void processATC(FGATCInstruction instruction);
+    FGAISchedule * getTrafficRef() { return trafficRef; };
 
     virtual const char* getTypeString(void) const { return "aircraft"; }
 
@@ -82,6 +83,7 @@ public:
     inline double getVerticalSpeed() const { return vs; };
     inline double altitudeAGL() const { return props->getFloatValue("position/altitude-agl-ft");};
     inline double airspeed() const { return props->getFloatValue("velocities/airspeed-kt");};
+    string atGate();
     
 protected:
     void Run(double dt);
@@ -138,6 +140,7 @@ private:
     bool _getGearDown() const;
 
     bool reachedWaypoint;
+    time_t timeElapsed;
 
     PerformanceData* _performance; // the performance data for this aircraft
 };
index e83e0dcbd85aa3dd2ebc12e7cb4cac5701d8e9b1..a2deb0a3c2e22292f00d496003d27c1d4553f611 100644 (file)
@@ -41,6 +41,7 @@ void FGAIFlightPlan::createPushBack(bool firstFlight, FGAirport *dep,
                                radius, fltType, aircraftType, airline);
     } else {
         if (firstFlight) {
+             
              if (!(dep->getDynamics()->getAvailableParking(&lat, &lon, 
                                                            &heading, &gateId, 
                                                            radius, fltType, 
@@ -70,6 +71,12 @@ void FGAIFlightPlan::createPushBack(bool firstFlight, FGAirport *dep,
                    wpt->routeIndex = -1;
                    waypoints.push_back(wpt);
             }
+           //cerr << "Success : GateId = " << gateId << endl;
+           SG_LOG(SG_INPUT, SG_WARN, "Warning: Succesfully found a parking for a " << 
+                                              aircraftType <<
+                                              " of flight type " << fltType << 
+                                              " of airline     " << airline <<
+                                              " at airport     " << dep->getId());
         } else {
            //cerr << "Push Back follow-up Flight" << endl;
             dep->getDynamics()->getParking(gateId, &lat, &lon, &heading);
@@ -89,61 +96,21 @@ void FGAIFlightPlan::createPushBack(bool firstFlight, FGAirport *dep,
        FGParking *parking = dep->getDynamics()->getParking(gateId);
         int pushBackNode = parking->getPushBackPoint();
 
-       // initialize the pushback route. Note that parts
-       // of this procedure should probably be done inside
-        // taxidraw. This code is likely to change once this
-        // this is fully implemented in taxidraw. Until that time,
-        // however, the full initialization procedure looks like this:
-        // 1) Build a list of all the nodes that are classified as 
-        //    pushBack hold points
-        // 2) For each hold point, use the dykstra algorithm to find a route
-        //    between the gate and the pushBack hold nodes, however use only
-        //    segments that are classified as "pushback" routes.
-        // 3) return the TaxiRoute class that is non empty. 
-        // 4) store refer this route in the parking object, for future use
-
-       if (pushBackNode < 0) {
-            //cerr << "Initializing PushBackRoute " << endl;
-            intVec pushBackNodes;
-           int nAINodes = dep->getDynamics()->getGroundNetwork()->getNrOfNodes();
-            int hits = 0;
-           parking->setPushBackPoint(0); // default in case no network was found.
 
-            // Collect all the nodes that are classified as having pushBack hold status
-            for (int i = 0; i < nAINodes; i++) {
-                if (dep->getDynamics()->getGroundNetwork()->findNode(i)->getHoldPointType() == 3) {
-                    pushBackNodes.push_back(i);
-                }
-           }
-
-            // For each node found in step 1, check if it can be reached
-            FGTaxiRoute route;
-            for (intVecIterator nodes = pushBackNodes.begin();
-                                 nodes != pushBackNodes.end();
-                                 nodes++) {
-               route = dep->getDynamics()->getGroundNetwork()->findShortestRoute(gateId, *nodes, false);
-                if (!(route.empty())) {
-                   //cerr << "Found Pushback route of size " << route.size() << endl;
-                    hits++;
-                    parking->setPushBackRoute(new FGTaxiRoute(route));
-                    parking->setPushBackPoint(*nodes);
-                   pushBackNode = *nodes;
-                }
-             }
-             if (hits == 0) {
-                 SG_LOG(SG_GENERAL, SG_INFO, "No pushback route found for gate " << gateId << " at " << dep->getId());
-             }
-             if (hits > 1) {
-                 SG_LOG(SG_GENERAL, SG_WARN, hits << " pushback routes found for gate " << gateId << " at " << dep->getId());
-             }
-        }
-        if (pushBackNode > 0) {
+        pushBackRoute = parking->getPushBackRoute();
+        if ((pushBackNode > 0) && (pushBackRoute == 0)) {
             int node, rte;
-            //cerr << "Found valid pusback node " << pushBackNode << "for gate " << gateId << endl;
+            FGTaxiRoute route;
+            //cerr << "Creating push-back for " << gateId << " (" << parking->getName() << ") using push-back point " << pushBackNode << endl;
+            route = dep->getDynamics()->getGroundNetwork()->findShortestRoute(gateId, pushBackNode, false);
+            parking->setPushBackRoute(new FGTaxiRoute(route));
+            
+
             pushBackRoute = parking->getPushBackRoute();
             int size = pushBackRoute->size();
             if (size < 2) {
                SG_LOG(SG_GENERAL, SG_WARN, "Push back route from gate " << gateId << " has only " << size << " nodes.");
+                SG_LOG(SG_GENERAL, SG_WARN, "Using  " << pushBackNode);
             }
             pushBackRoute->first();
             waypoint *wpt;
diff --git a/src/ATC/Makefile.am b/src/ATC/Makefile.am
new file mode 100644 (file)
index 0000000..32e4ced
--- /dev/null
@@ -0,0 +1,6 @@
+noinst_LIBRARIES = libATC.a
+
+libATC_a_SOURCES = \
+       trafficcontrol.cxx trafficcontrol.hxx 
+
+INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx
new file mode 100644 (file)
index 0000000..85270f3
--- /dev/null
@@ -0,0 +1,787 @@
+// trafficrecord.cxx - Implementation of AIModels ATC code.
+//
+// Written by Durk Talsma, started September 2006.
+//
+// Copyright (C) 2006 Durk Talsma.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "trafficcontrol.hxx"
+#include <AIModel/AIAircraft.hxx>
+#include <AIModel/AIFlightPlan.hxx>
+#include <Traffic/TrafficMgr.hxx>
+
+
+/***************************************************************************
+ * FGTrafficRecord
+ **************************************************************************/
+FGTrafficRecord::FGTrafficRecord() :
+  id(0), waitsForId(0),
+  currentPos(0),
+  leg(0),
+  state(0),
+  latitude(0),
+  longitude(0), 
+   heading(0), 
+   speed(0), 
+   altitude(0), 
+   radius(0) {
+}
+
+void FGTrafficRecord::setPositionAndIntentions(int pos, FGAIFlightPlan *route)
+{
+   currentPos = pos;
+   if (intentions.size()) {
+     intVecIterator i = intentions.begin();
+     if ((*i) != pos) {
+       SG_LOG(SG_GENERAL, SG_ALERT, "Error in FGTrafficRecord::setPositionAndIntentions");
+       //cerr << "Pos : " << pos << " Curr " << *(intentions.begin())  << endl;
+       for (intVecIterator i = intentions.begin(); i != intentions.end() ; i++) {
+       //cerr << (*i) << " ";
+       }
+       //cerr << endl;
+     }
+     intentions.erase(i);
+   } else {
+     //int legNr, routeNr;
+     //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
+     int size = route->getNrOfWayPoints();
+     //cerr << "Setting pos" << pos << " ";
+     //cerr << "setting intentions ";
+     for (int i = 0; i < size; i++) {
+       int val = route->getRouteIndex(i);
+       //cerr << val<< " ";
+       if ((val) && (val != pos))
+       {
+         intentions.push_back(val); 
+         //cerr << "[set] ";
+       }
+     }
+     //cerr << endl;
+     //while (route->next(&legNr, &routeNr)) {
+     //intentions.push_back(routeNr);
+     //}
+     //route->rewind(currentPos);
+   }
+   //exit(1);
+}
+
+bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord &other)
+{
+   bool result = false;
+   //cerr << "Start check 1" << endl;
+   if (currentPos == other.currentPos) 
+     {
+       //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl;
+       result = true;
+     }
+  //  else if (other.intentions.size()) 
+ //     {
+ //       cerr << "Start check 2" << endl;
+ //       intVecIterator i = other.intentions.begin(); 
+ //       while (!((i == other.intentions.end()) || ((*i) == currentPos)))
+ //    i++;
+ //       if (i != other.intentions.end()) {
+ //    cerr << "Check Position and intentions: current matches other.intentions" << endl;
+ //    result = true;
+ //       }
+   else if (intentions.size()) {
+     //cerr << "Start check 3" << endl;
+     intVecIterator i = intentions.begin(); 
+     //while (!((i == intentions.end()) || ((*i) == other.currentPos)))
+     while (i != intentions.end()) {
+       if ((*i) == other.currentPos) {
+        break;
+       }
+       i++;
+     }
+     if (i != intentions.end()) {
+       //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl;
+       result = true;
+     }
+   }
+   //cerr << "Done !!" << endl;
+   return result;
+}
+
+void FGTrafficRecord::setPositionAndHeading(double lat, double lon, double hdg, 
+                                           double spd, double alt)
+{
+  latitude = lat;
+  longitude = lon;
+  heading = hdg;
+  speed = spd;
+  altitude = alt;
+}
+
+int FGTrafficRecord::crosses(FGGroundNetwork *net, FGTrafficRecord &other)
+{
+   if (checkPositionAndIntentions(other) || (other.checkPositionAndIntentions(*this)))
+     return -1;
+   intVecIterator i, j;
+   int currentTargetNode = 0, otherTargetNode = 0;
+   if (currentPos > 0)
+     currentTargetNode = net->findSegment(currentPos      )->getEnd()->getIndex(); // OKAY,... 
+   if (other.currentPos > 0)
+     otherTargetNode   = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,...
+   if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
+     return currentTargetNode;
+   if (intentions.size())
+     {
+       for (i = intentions.begin(); i != intentions.end(); i++)
+       {
+         if ((*i) > 0) {
+           if ((currentTargetNode == net->findSegment(*i)->getEnd()->getIndex()))
+             {
+               //cerr << "Current crosses at " << currentTargetNode <<endl;
+               return currentTargetNode;
+             }
+         }
+       }
+     }
+   if (other.intentions.size())
+     {
+       for (i = other.intentions.begin(); i != other.intentions.end(); i++)
+       {
+         if ((*i) > 0) {
+           if (otherTargetNode == net->findSegment(*i)->getEnd()->getIndex())
+             {
+               //cerr << "Other crosses at " << currentTargetNode <<endl;
+               return otherTargetNode;
+             }
+         }
+       }
+     }
+   if (intentions.size() && other.intentions.size())
+     {
+       for (i = intentions.begin(); i != intentions.end(); i++) 
+       {
+         for (j = other.intentions.begin(); j != other.intentions.end(); j++)
+           {
+             //cerr << "finding segment " << *i << " and " << *j << endl;
+             if (((*i) > 0) && ((*j) > 0)) {
+               currentTargetNode = net->findSegment(*i)->getEnd()->getIndex();
+               otherTargetNode   = net->findSegment(*j)->getEnd()->getIndex();
+               if (currentTargetNode == otherTargetNode) 
+                 {
+                   //cerr << "Routes will cross at " << currentTargetNode << endl;
+                   return currentTargetNode;
+                 }
+             }
+           }
+       }
+     }
+  return -1;
+}
+
+bool FGTrafficRecord::onRoute(FGGroundNetwork *net, FGTrafficRecord &other)
+{
+  int node = -1, othernode = -1;
+  if (currentPos >0)
+    node = net->findSegment(currentPos)->getEnd()->getIndex();
+  if (other.currentPos > 0)
+    othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
+  if ((node == othernode) && (node != -1))
+    return true;
+  if (other.intentions.size())
+    {
+      for (intVecIterator i = other.intentions.begin(); i != other.intentions.end(); i++)
+       {
+         if (*i > 0) 
+           {
+             othernode = net->findSegment(*i)->getEnd()->getIndex();
+             if ((node == othernode) && (node > -1))
+               return true;
+           }
+       }
+    }
+  //if (other.currentPos > 0)
+  //  othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
+  //if (intentions.size())
+  //  {
+  //    for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
+  //   {
+  //     if (*i > 0) 
+  //       {
+  //         node = net->findSegment(*i)->getEnd()->getIndex();
+  //         if ((node == othernode) && (node > -1))
+  //           return true;
+  //       }
+  //   }
+  //  }
+  return false;
+}
+
+
+bool FGTrafficRecord::isOpposing (FGGroundNetwork *net, FGTrafficRecord &other, int node)
+{
+   // Check if current segment is the reverse segment for the other aircraft
+   FGTaxiSegment *opp;
+   //cerr << "Current segment " << currentPos << endl;
+   if ((currentPos > 0) && (other.currentPos > 0))
+     {
+       opp = net->findSegment(currentPos)->opposite();
+       if (opp) {
+       if (opp->getIndex() == other.currentPos)
+         return true;
+       }
+      
+       for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
+       {
+         if (opp = net->findSegment(other.currentPos)->opposite())
+           {
+             if ((*i) > 0)
+               if (opp->getIndex() == net->findSegment(*i)->getIndex())
+                 {
+                   if (net->findSegment(*i)->getStart()->getIndex() == node) {
+                     {
+                       //cerr << "Found the node " << node << endl;
+                       return true;
+                     }
+                   }
+                 }
+           }
+         if (other.intentions.size())
+           {
+             for (intVecIterator j = other.intentions.begin(); j != other.intentions.end(); j++)
+               {  
+                 // cerr << "Current segment 1 " << (*i) << endl;
+                 if ((*i) > 0) {
+                   if (opp = net->findSegment(*i)->opposite())
+                     {
+                       if (opp->getIndex() == 
+                           net->findSegment(*j)->getIndex())
+                         {
+                           //cerr << "Nodes " << net->findSegment(*i)->getIndex()
+                           //   << " and  " << net->findSegment(*j)->getIndex()
+                           //   << " are opposites " << endl;
+                           if (net->findSegment(*i)->getStart()->getIndex() == node) {
+                             {
+                               //cerr << "Found the node " << node << endl;
+                               return true;
+                             }
+                           }
+                         }
+                     }
+                 }
+               }
+           }
+       }
+     }
+   return false;
+}
+
+void FGTrafficRecord::setSpeedAdjustment(double spd) 
+{ 
+  instruction.setChangeSpeed(true); 
+  instruction.setSpeed(spd); 
+}
+
+void FGTrafficRecord::setHeadingAdjustment(double heading) 
+{ 
+  instruction.setChangeHeading(true);
+  instruction.setHeading(heading); 
+}
+
+
+
+/***************************************************************************
+ * FGATCInstruction
+ *
+ **************************************************************************/
+FGATCInstruction::FGATCInstruction()
+{
+  holdPattern    = false; 
+  holdPosition   = false;
+  changeSpeed    = false;
+  changeHeading  = false;
+  changeAltitude = false;
+  resolveCircularWait = false;
+
+  double speed   = 0;
+  double heading = 0;
+  double alt     = 0;
+}
+
+bool FGATCInstruction::hasInstruction()
+{
+  return (holdPattern || holdPosition || changeSpeed || changeHeading || changeAltitude || resolveCircularWait);
+}
+
+string FGATCController::getGateName(FGAIAircraft *ref) 
+{
+    return ref->atGate();
+}
+
+void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir msgDir)
+{
+    string sender, receiver;
+    int commFreq = 0;
+    //double commFreqD;
+    switch (msgDir) {
+         case ATC_AIR_TO_GROUND:
+             sender = rec->getAircraft()->getTrafficRef()->getCallSign();
+             switch (rec->getLeg()) {
+                 case 2:
+                     commFreq =
+                        rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency();
+                     receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
+                     break;
+                 case 3: 
+                     receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
+                     break;
+                 case 4: 
+                     receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
+                     break;
+             }
+             break;
+         case ATC_GROUND_TO_AIR:
+             receiver = rec->getAircraft()->getTrafficRef()->getCallSign();
+             switch (rec->getLeg()) {
+                 case 2:
+                 commFreq =
+                        rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency();
+                     sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
+                     break;
+                 case 3: 
+                     sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground";
+                     break;
+                 case 4: 
+                     sender = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower";
+                     break;
+             }
+             break;
+         }
+    string text;
+    switch (msgId) {
+          case MSG_ANNOUNCE_ENGINE_START:
+               text = sender + ". Ready to Start up";
+               break;
+          case MSG_REQUEST_ENGINE_START:
+               text = receiver + ", This is " + sender + ". Position " +getGateName(rec->getAircraft()) +
+                       ". Information YY." +
+                       rec->getAircraft()->getTrafficRef()->getFlightRules() + " to " + 
+                       rec->getAircraft()->getTrafficRef()->getArrivalAirport()->getName() + ". Request start-up";
+               break;
+          case MSG_PERMIT_ENGINE_START:
+               text = receiver + ". Start-up approved. YY correct, runway ZZ, AAA departure, squawk BBBB. " +
+                      "For push-back and taxi clearance call CCC.CCC. " + sender + " control.";
+               break;
+          case MSG_DENY_ENGINE_START:
+               text = receiver + ". Standby";
+               break;
+         case MSG_ACKNOWLEDGE_ENGINE_START:
+               text = receiver + ". Start-up approved. YY correct, runway ZZ, AAA departure, squawk BBBB. " +
+                      "For push-back and taxi clearance call CCC.CCC. " + sender;
+               break;
+           default:
+               break;
+    }
+    double currFreq = fgGetDouble("/instrumentation/comm/frequencies/selected-mhz");
+    int currFreqI = (int) round(currFreq * 100);
+    //cerr << "Using " << currFreqI << " and " << commFreq << endl;
+    if (currFreqI == commFreq) {
+        fgSetString("/sim/messages/atc", text.c_str());
+        //cerr << "Printing Message: " << endl;
+    }
+}
+
+
+/***************************************************************************
+ * class FGTowerController
+ *
+ **************************************************************************/
+FGTowerController::FGTowerController() :
+  FGATCController()
+{
+}
+
+// 
+void FGTowerController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
+                                        double lat, double lon, double heading, 
+                                        double speed, double alt, double radius, int leg,
+                                        FGAIAircraft *ref)
+{
+  TrafficVectorIterator i = activeTraffic.begin();
+  // Search whether the current id alread has an entry
+  // This might be faster using a map instead of a vector, but let's start by taking a safe route
+  if (activeTraffic.size()) {
+    //while ((i->getId() != id) && i != activeTraffic.end()) {
+    while (i != activeTraffic.end()) {
+      if (i->getId() == id) {
+       break;
+      }
+      i++;
+    }
+  }
+  
+  // Add a new TrafficRecord if no one exsists for this aircraft.
+  if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
+    FGTrafficRecord rec;
+    rec.setId(id);
+
+    rec.setPositionAndHeading(lat, lon, heading, speed, alt);
+    rec.setRunway(intendedRoute->getRunway());
+    rec.setLeg(leg);
+    //rec.setCallSign(callsign);
+    rec.setAircraft(ref);
+    activeTraffic.push_back(rec);
+  } else {
+    i->setPositionAndHeading(lat, lon, heading, speed, alt);
+  }
+}
+
+void FGTowerController::update(int id, double lat, double lon, double heading, double speed, double alt, 
+                            double dt)
+{
+    TrafficVectorIterator i = activeTraffic.begin();
+    // Search search if the current id has an entry
+    // This might be faster using a map instead of a vector, but let's start by taking a safe route
+    TrafficVectorIterator current, closest;
+    if (activeTraffic.size()) {
+      //while ((i->getId() != id) && i != activeTraffic.end()) {
+      while (i != activeTraffic.end()) {
+       if (i->getId() == id) {
+         break;
+       }
+        i++;
+      }
+    }
+
+//    // update position of the current aircraft
+    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
+      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
+    } else {
+      i->setPositionAndHeading(lat, lon, heading, speed, alt);
+      current = i;
+    }
+    setDt(getDt() + dt);
+
+//    // see if we already have a clearance record for the currently active runway
+    ActiveRunwayVecIterator rwy = activeRunways.begin();
+    // again, a map might be more efficient here
+    if (activeRunways.size()) {
+      //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) {
+      while (rwy != activeRunways.end()) {
+       if (rwy->getRunwayName() == current->getRunway()) {
+         break;
+       }
+        rwy++;
+      }
+    }
+    if (rwy == activeRunways.end()) {
+      ActiveRunway aRwy(current->getRunway(), id);
+      activeRunways.push_back(aRwy);   // Since there are no clearance records for this runway yet
+      current->setHoldPosition(false); // Clear the current aircraft to continue
+    }
+    else {
+      // Okay, we have a clearance record for this runway, so check
+      // whether the clearence ID matches that of the current aircraft
+      if (id == rwy->getCleared()) {
+        current->setHoldPosition(false);
+      } else {
+        current->setHoldPosition(true);
+      }
+    }
+}
+
+
+void FGTowerController::signOff(int id) 
+{
+  TrafficVectorIterator i = activeTraffic.begin();
+  // Search search if the current id alread has an entry
+  // This might be faster using a map instead of a vector, but let's start by taking a safe route
+    if (activeTraffic.size()) {
+      //while ((i->getId() != id) && i != activeTraffic.end()) {
+      while (i != activeTraffic.end()) {
+       if (i->getId() == id) {
+         break;
+       }
+        i++;
+      }
+    }
+    // If this aircraft has left the runway, we can clear the departure record for this runway
+    ActiveRunwayVecIterator rwy = activeRunways.begin();
+    if (activeRunways.size()) {
+      //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
+      while (rwy != activeRunways.end()) {
+        if (rwy->getRunwayName() == i->getRunway()) {
+         break;
+       }
+        rwy++;
+      }
+      if (rwy != activeRunways.end()) {
+        rwy = activeRunways.erase(rwy);
+      } else {
+        SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
+      }
+    }
+    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
+      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower");
+    } else {
+      i = activeTraffic.erase(i);
+    }
+}
+
+// NOTE:
+// IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
+// THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
+// BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
+// WHICH WOULD SIMPLIFY CODE MAINTENANCE.
+// Note that this function is probably obsolete
+bool FGTowerController::hasInstruction(int id)
+{
+    TrafficVectorIterator i = activeTraffic.begin();
+    // Search search if the current id has an entry
+    // This might be faster using a map instead of a vector, but let's start by taking a safe route
+    if (activeTraffic.size()) 
+      {
+        //while ((i->getId() != id) && i != activeTraffic.end()) {
+       while (i != activeTraffic.end()) {
+         if (i->getId() == id) {
+           break;
+         }
+        i++;
+      }
+    }
+    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
+      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
+    } else {
+      return i->hasInstruction();
+    }
+  return false;
+}
+
+
+FGATCInstruction FGTowerController::getInstruction(int id)
+{
+  TrafficVectorIterator i = activeTraffic.begin();
+  // Search search if the current id has an entry
+  // This might be faster using a map instead of a vector, but let's start by taking a safe route
+  if (activeTraffic.size()) {
+    //while ((i->getId() != id) && i != activeTraffic.end()) {
+    while (i != activeTraffic.end()) {
+      if (i->getId() == id) {
+       break;
+      }
+      i++;
+    }
+  }
+  if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
+    SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
+  } else {
+    return i->getInstruction();
+  }
+  return FGATCInstruction();
+}
+
+/***************************************************************************
+ * class FGStartupController
+ *
+ **************************************************************************/
+FGStartupController::FGStartupController() :
+  FGATCController()
+{
+    available = true;
+}
+
+void FGStartupController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
+                                        double lat, double lon, double heading, 
+                                        double speed, double alt, double radius, int leg,
+                                        FGAIAircraft *ref)
+{
+  TrafficVectorIterator i = activeTraffic.begin();
+  // Search whether the current id alread has an entry
+  // This might be faster using a map instead of a vector, but let's start by taking a safe route
+  if (activeTraffic.size()) {
+    //while ((i->getId() != id) && i != activeTraffic.end()) {
+    while (i != activeTraffic.end()) {
+      if (i->getId() == id) {
+       break;
+      }
+      i++;
+    }
+  }
+  
+  // Add a new TrafficRecord if no one exsists for this aircraft.
+  if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
+    FGTrafficRecord rec;
+    rec.setId(id);
+
+    rec.setPositionAndHeading(lat, lon, heading, speed, alt);
+    rec.setRunway(intendedRoute->getRunway());
+    rec.setLeg(leg);
+    //rec.setCallSign(callsign);
+    rec.setAircraft(ref);
+    rec.setHoldPosition(true);
+    activeTraffic.push_back(rec);
+    } else {
+        i->setPositionAndHeading(lat, lon, heading, speed, alt);
+        
+  }
+}
+
+// NOTE:
+// IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
+// THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN 
+// BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
+// WHICH WOULD SIMPLIFY CODE MAINTENANCE.
+// Note that this function is probably obsolete
+bool FGStartupController::hasInstruction(int id)
+{
+    TrafficVectorIterator i = activeTraffic.begin();
+    // Search search if the current id has an entry
+    // This might be faster using a map instead of a vector, but let's start by taking a safe route
+    if (activeTraffic.size()) 
+      {
+        //while ((i->getId() != id) && i != activeTraffic.end()) {
+       while (i != activeTraffic.end()) {
+         if (i->getId() == id) {
+           break;
+         }
+        i++;
+      }
+    }
+    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
+      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
+    } else {
+      return i->hasInstruction();
+    }
+  return false;
+}
+
+
+FGATCInstruction FGStartupController::getInstruction(int id)
+{
+  TrafficVectorIterator i = activeTraffic.begin();
+  // Search search if the current id has an entry
+  // This might be faster using a map instead of a vector, but let's start by taking a safe route
+  if (activeTraffic.size()) {
+    //while ((i->getId() != id) && i != activeTraffic.end()) {
+    while (i != activeTraffic.end()) {
+      if (i->getId() == id) {
+       break;
+      }
+      i++;
+    }
+  }
+  if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
+    SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
+  } else {
+    return i->getInstruction();
+  }
+  return FGATCInstruction();
+}
+
+void FGStartupController::signOff(int id) 
+{
+  TrafficVectorIterator i = activeTraffic.begin();
+  // Search search if the current id alread has an entry
+  // This might be faster using a map instead of a vector, but let's start by taking a safe route
+    if (activeTraffic.size()) {
+      //while ((i->getId() != id) && i != activeTraffic.end()) {
+      while (i != activeTraffic.end()) {
+       if (i->getId() == id) {
+         break;
+       }
+        i++;
+      }
+    }
+    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
+      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower");
+    } else {
+      i = activeTraffic.erase(i);
+    }
+}
+
+void FGStartupController::update(int id, double lat, double lon, double heading, double speed, double alt, 
+                            double dt)
+{
+    TrafficVectorIterator i = activeTraffic.begin();
+    // Search search if the current id has an entry
+    // This might be faster using a map instead of a vector, but let's start by taking a safe route
+    TrafficVectorIterator current, closest;
+    if (activeTraffic.size()) {
+      //while ((i->getId() != id) && i != activeTraffic.end()) {
+      while (i != activeTraffic.end()) {
+       if (i->getId() == id) {
+         break;
+       }
+        i++;
+      }
+    }
+
+//    // update position of the current aircraft
+    if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
+      SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
+    } else {
+      i->setPositionAndHeading(lat, lon, heading, speed, alt);
+      current = i;
+    }
+    setDt(getDt() + dt);
+
+    int state = i->getState();
+    time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
+    time_t now       = time(NULL) + fgGetLong("/sim/time/warp");
+
+    if ((now - lastTransmission) > 3 + (rand() % 15)) {
+        available = true;
+    }
+
+    if ((state == 0) && available) {
+        if (now > startTime) {
+             transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
+             i->updateState();
+             lastTransmission = now;
+             available = false;
+        }
+    }
+    if ((state == 1) && available) {
+        if (now > startTime+60) {
+            transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
+            i->updateState();
+            lastTransmission = now;
+             available = false;
+        }
+    }
+    if ((state == 2) && available) {
+        if (now > startTime+80) {
+            transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
+            i->updateState();
+            lastTransmission = now;
+             available = false;
+        }
+    }
+    if ((state == 3) && available){
+        if (now > startTime+100) {
+            transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND);
+            i->updateState();
+            lastTransmission = now;
+             available = false;
+        }
+     }
+     // TODO: Switch to APRON control and request pushback Clearance.
+     // Get Push back clearance
+     if ((state == 4) && available){
+          i->setHoldPosition(false);
+     }
+}
diff --git a/src/ATC/trafficcontrol.hxx b/src/ATC/trafficcontrol.hxx
new file mode 100644 (file)
index 0000000..f2e63ef
--- /dev/null
@@ -0,0 +1,292 @@
+// trafficcontrol.hxx - classes to manage AIModels based air traffic control
+// Written by Durk Talsma, started September 2006.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+
+#ifndef _TRAFFIC_CONTROL_HXX_
+#define _TRAFFIC_CONTROL_HXX_
+
+
+#ifndef __cplusplus
+# error This library requires C++
+#endif
+
+
+#include <simgear/compiler.h>
+#include <simgear/debug/logstream.hxx>
+
+
+
+#include STL_STRING
+#include <vector>
+
+SG_USING_STD(string);
+SG_USING_STD(vector);
+
+
+typedef vector<int> intVec;
+typedef vector<int>::iterator intVecIterator;
+
+
+class FGAIFlightPlan;  // forward reference
+class FGGroundNetwork; // forward reference
+//class FGAISchedule;    // forward reference
+class FGAIAircraft;    // forward reference
+
+/**************************************************************************************
+ * class FGATCInstruction
+ * like class FGATC Controller, this class definition should go into its own file
+ * and or directory... For now, just testing this stuff out though...
+ *************************************************************************************/
+class FGATCInstruction
+{
+private:
+  bool holdPattern;
+  bool holdPosition;
+  bool changeSpeed;
+  bool changeHeading;
+  bool changeAltitude;
+  bool resolveCircularWait;
+
+  double speed;
+  double heading;
+  double alt;
+public:
+
+  FGATCInstruction();
+  bool hasInstruction   ();
+  bool getHoldPattern   () { return holdPattern;    };
+  bool getHoldPosition  () { return holdPosition;   };
+  bool getChangeSpeed   () { return changeSpeed;    };
+  bool getChangeHeading () { return changeHeading;  };
+  bool getChangeAltitude() { return changeAltitude; };
+
+  double getSpeed       () { return speed; };
+  double getHeading     () { return heading; };
+  double getAlt         () { return alt; };
+
+  bool getCheckForCircularWait() { return resolveCircularWait; };
+
+  void setHoldPattern   (bool val) { holdPattern    = val; };
+  void setHoldPosition  (bool val) { holdPosition   = val; };
+  void setChangeSpeed   (bool val) { changeSpeed    = val; };
+  void setChangeHeading (bool val) { changeHeading  = val; };
+  void setChangeAltitude(bool val) { changeAltitude = val; };
+
+  void setResolveCircularWait (bool val) { resolveCircularWait = val; }; 
+
+  void setSpeed       (double val) { speed   = val; };
+  void setHeading     (double val) { heading = val; };
+  void setAlt         (double val) { alt     = val; };
+};
+
+
+
+
+
+/**************************************************************************************
+ * class FGTrafficRecord
+ *************************************************************************************/
+class FGTrafficRecord
+{
+private:
+  int id, waitsForId;
+  int currentPos;
+  int leg;
+  int state;
+  time_t timer;
+  intVec intentions;
+  FGATCInstruction instruction;
+  double latitude, longitude, heading, speed, altitude, radius;
+  string runway;
+  //FGAISchedule *trafficRef;
+  FGAIAircraft *aircraft;
+  
+  
+public:
+  FGTrafficRecord();
+  
+  void setId(int val)  { id = val; };
+  void setRadius(double rad) { radius = rad;};
+  void setPositionAndIntentions(int pos, FGAIFlightPlan *route);
+  void setRunway(string rwy) { runway = rwy;};
+  void setLeg(int lg) { leg = lg;};
+  int getId() { return id;};
+  int getState() { return state;};
+  FGATCInstruction getInstruction() { return instruction;};
+  bool hasInstruction() { return instruction.hasInstruction(); };
+  void setPositionAndHeading(double lat, double lon, double hdg, double spd, double alt);
+  bool checkPositionAndIntentions(FGTrafficRecord &other);
+  int  crosses                   (FGGroundNetwork *, FGTrafficRecord &other); 
+  bool isOpposing                (FGGroundNetwork *, FGTrafficRecord &other, int node);
+
+  bool onRoute(FGGroundNetwork *, FGTrafficRecord &other);
+
+  bool getSpeedAdjustment() { return instruction.getChangeSpeed(); };
+  
+  double getLatitude () { return latitude ; };
+  double getLongitude() { return longitude; };
+  double getHeading  () { return heading  ; };
+  double getSpeed    () { return speed    ; };
+  double getAltitude () { return altitude ; };
+  double getRadius   () { return radius   ; };
+
+  int getWaitsForId  () { return waitsForId; };
+
+  void setSpeedAdjustment(double spd);
+  void setHeadingAdjustment(double heading);
+  void clearSpeedAdjustment  () { instruction.setChangeSpeed  (false); };
+  void clearHeadingAdjustment() { instruction.setChangeHeading(false); };
+
+  bool hasHeadingAdjustment() { return instruction.getChangeHeading(); };
+  bool hasHoldPosition() { return instruction.getHoldPosition(); };
+  void setHoldPosition (bool inst) { instruction.setHoldPosition(inst); };
+
+  void setWaitsForId(int id) { waitsForId = id; };
+
+  void setResolveCircularWait()   { instruction.setResolveCircularWait(true);  };
+  void clearResolveCircularWait() { instruction.setResolveCircularWait(false); };
+
+  string getRunway() { return runway; };
+  //void setCallSign(string clsgn) { callsign = clsgn; };
+  void setAircraft(FGAIAircraft *ref) { aircraft = ref;};
+  void updateState() { state++;};
+  //string getCallSign() { return callsign; };
+  FGAIAircraft *getAircraft() { return aircraft;};
+  int getTime() { return timer; };
+  int getLeg() { return leg; };
+  void setTime(time_t time) { timer = time; };
+};
+
+typedef vector<FGTrafficRecord> TrafficVector;
+typedef vector<FGTrafficRecord>::iterator TrafficVectorIterator;
+
+
+/***********************************************************************
+ * Active runway, a utility class to keep track of which aircraft has
+ * clearance for a given runway.
+ **********************************************************************/
+class ActiveRunway
+{
+private:
+  string rwy;
+  int currentlyCleared;
+public:
+  ActiveRunway(string r, int cc) { rwy = r; currentlyCleared = cc; };
+  
+  string getRunwayName() { return rwy; };
+  int    getCleared   () { return currentlyCleared; };
+};
+
+typedef vector<ActiveRunway> ActiveRunwayVec;
+typedef vector<ActiveRunway>::iterator ActiveRunwayVecIterator;
+
+/**
+ * class FGATCController
+ * NOTE: this class serves as an abstraction layer for all sorts of ATC controller. 
+ *************************************************************************************/
+class FGATCController
+{
+private:
+  double dt_count;
+public:
+  typedef enum {
+      MSG_ANNOUNCE_ENGINE_START,
+      MSG_REQUEST_ENGINE_START, 
+      MSG_PERMIT_ENGINE_START,
+      MSG_DENY_ENGINE_START,
+      MSG_ACKNOWLEDGE_ENGINE_START } AtcMsgId;
+  typedef enum {
+      ATC_AIR_TO_GROUND,
+      ATC_GROUND_TO_AIR } AtcMsgDir;
+  FGATCController() { dt_count = 0;};
+  virtual ~FGATCController() {};
+  virtual void announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute,
+                               double lat, double lon,
+                               double hdg, double spd, double alt, double radius, int leg,
+                               FGAIAircraft *aircraft) = 0;
+  virtual void             signOff(int id) = 0;
+  virtual void             update(int id, double lat, double lon, 
+                                 double heading, double speed, double alt, double dt) = 0;
+  virtual bool             hasInstruction(int id) = 0;
+  virtual FGATCInstruction getInstruction(int id) = 0;
+
+  double getDt() { return dt_count; };
+  void   setDt(double dt) { dt_count = dt;};
+  void transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir msgDir);
+  string getGateName(FGAIAircraft *aircraft);
+};
+
+/******************************************************************************
+ * class FGTowerControl
+ *****************************************************************************/
+class FGTowerController : public FGATCController
+{
+private:
+  TrafficVector activeTraffic;
+  ActiveRunwayVec activeRunways;
+  
+public:
+  FGTowerController();
+  virtual ~FGTowerController() {};
+  virtual void announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute,
+                               double lat, double lon,
+                               double hdg, double spd, double alt, double radius, int leg,
+                               FGAIAircraft *aircraft);
+  virtual void             signOff(int id);
+  virtual void             update(int id, double lat, double lon, 
+                                 double heading, double speed, double alt, double dt);
+  virtual bool             hasInstruction(int id);
+  virtual FGATCInstruction getInstruction(int id);
+
+  bool hasActiveTraffic() { return activeTraffic.size() != 0; };
+  TrafficVector &getActiveTraffic() { return activeTraffic; };
+};
+
+/******************************************************************************
+ * class FGStartupController
+ * handle 
+ *****************************************************************************/
+
+class FGStartupController : public FGATCController
+{
+private:
+  TrafficVector activeTraffic;
+  bool available;
+  time_t lastTransmission;
+  //ActiveRunwayVec activeRunways;
+  
+public:
+  FGStartupController();
+  virtual ~FGStartupController() {};
+  virtual void announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute,
+                               double lat, double lon,
+                               double hdg, double spd, double alt, double radius, int leg,
+                               FGAIAircraft *aircraft);
+  virtual void             signOff(int id);
+  virtual void             update(int id, double lat, double lon, 
+                                 double heading, double speed, double alt, double dt);
+  virtual bool             hasInstruction(int id);
+  virtual FGATCInstruction getInstruction(int id);
+
+  bool hasActiveTraffic() { return activeTraffic.size() != 0; };
+  TrafficVector &getActiveTraffic() { return activeTraffic; };
+
+}; 
+
+#endif // _TRAFFIC_CONTROL_HXX
index c560d1643605eb3b973a52aa153dea45a5d6d615..a7c199283665db5332d0d6ee7a550eb05e86eeb9 100644 (file)
@@ -1,6 +1,6 @@
-noinst_LIBRARIES = libATC.a
+noinst_LIBRARIES = libATCDCL.a
 
-libATC_a_SOURCES = \
+libATCDCL_a_SOURCES = \
        ATC.hxx ATC.cxx \
        atis.hxx atis.cxx \
        tower.hxx tower.cxx \
index 5cc6abe152199638f3510f7580d0b15679ec4429..a87d26c49d2f9752a15cb541361bf924488eef15 100644 (file)
@@ -11,7 +11,6 @@ libAirports_a_SOURCES = \
        gnnode.cxx gnnode.hxx \
        groundnetwork.cxx groundnetwork.hxx \
        dynamics.cxx dynamics.hxx \
-       trafficcontrol.cxx trafficcontrol.hxx \
        dynamicloader.cxx dynamicloader.hxx \
        runwayprefloader.cxx runwayprefloader.hxx \
        xmlloader.cxx xmlloader.hxx 
index 986b1dd39bc120307cf237c157e3b0615499aa54..3f6733be147af1e8c7044817e0c5d45ceebf32f6 100644 (file)
@@ -34,6 +34,7 @@ void  FGAirportDynamicsXMLLoader::startElement (const char * name, const XMLAttr
   FGTaxiNode taxiNode;
   FGTaxiSegment taxiSegment;
   int index = 0;
+  string idxStr;
   taxiSegment.setIndex(index);
   //cout << "Start element " << name << endl;
   string attname;
@@ -44,15 +45,19 @@ void  FGAirportDynamicsXMLLoader::startElement (const char * name, const XMLAttr
   string lat;
   string lon;
   int holdPointType;
+  int pushBackPoint;
   
   if (name == string("Parking"))
     {
+      pushBackPoint = 0;
       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(std::atoi(atts.getValue(i)));
+         if (attname == string("index")) {
+               park.setIndex(std::atoi(atts.getValue(i)));
+                idxStr = atts.getValue(i);
+          }
          else if (attname == string("type"))
            park.setType(atts.getValue(i));
         else if (attname == string("name"))
@@ -72,10 +77,17 @@ void  FGAirportDynamicsXMLLoader::startElement (const char * name, const XMLAttr
            //cerr << "Radius " << radius <<endl;
            park.setRadius(std::atof(radius.c_str()));
          }
-          else if (attname == string("airlineCodes"))
+         else if (attname == string("airlineCodes"))
             park.setCodes(atts.getValue(i));
+          else if (attname == string("pushBackRoute")) {
+             pushBackPoint = std::atoi(atts.getValue(i));
+            //park.setPushBackPoint(std::atoi(atts.getValue(i)));
+
+           }
        }
+      park.setPushBackPoint(pushBackPoint);
       park.setName((gateName+gateNumber));
+      //cerr << "Parking " << idxStr << "( " << gateName << gateNumber << ") has pushBackPoint " << pushBackPoint << endl;
       _dynamics->addParking(park);
     }
   if (name == string("node")) 
@@ -128,16 +140,41 @@ void  FGAirportDynamicsXMLLoader::startElement (const char * name, const XMLAttr
 
 void  FGAirportDynamicsXMLLoader::endElement (const char * name) {
   //cout << "End element " << name << endl;
+  if (name == string("AWOS")) {
+       _dynamics->addAwosFreq(atoi(value.c_str()));
+       //cerr << "Adding AWOS" << value<< endl;
+  }
+  if (name == string("UNICOM")) {
+       _dynamics->addUnicomFreq(atoi(value.c_str()));
+       //cerr << "UNICOM" << value<< endl;
+  }
+if (name == string("CLEARANCE")) {
+       _dynamics->addClearanceFreq(atoi(value.c_str()));
+       //cerr << "Adding CLEARANCE" << value<< endl;
+  }
+if (name == string("GROUND")) {
+       _dynamics->addGroundFreq(atoi(value.c_str()));
+       //cerr << "Adding GROUND" << value<< endl;
+  }
+
+if (name == string("TOWER")) {
+       _dynamics->addTowerFreq(atoi(value.c_str()));
+      //cerr << "Adding TOWER" << value<< endl;
+  }
+if (name == string("APPROACH")) {
+       _dynamics->addApproachFreq(atoi(value.c_str()));
+       //cerr << "Adding approach" << value<< endl;
+  }
 
 }
 
 void  FGAirportDynamicsXMLLoader::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("");
+  if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos))
+    value += token;
+  else
+    value = string("");
 }
 
 void  FGAirportDynamicsXMLLoader::pi (const char * target, const char * data) {
index 8f86cf6abca508a699a1d226c770a7b7f25901d0..672cda5af7c577be1840f423d3b71de0cd9182af 100644 (file)
@@ -36,6 +36,7 @@ protected:
 
 private:
     FGAirportDynamics* _dynamics;
+    string value;
 };
 
 #endif
index 0123013cac4b6b4628c6d2a9c60c1b851057a5ef..4cb904abf4f782589400e7cc5be9fa53ba128a74 100644 (file)
@@ -205,10 +205,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)
            {
@@ -231,7 +231,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++)
        {
@@ -303,18 +303,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() && (i >= 0)) 
-    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)
index 0e5d6f4383e79cdf0257efe9a97b791ed6b558b7..f6e344932758447a2af8a60635ea2b64efd416aa 100644 (file)
 
 #include <simgear/xml/easyxml.hxx>
 
+#include <ATC/trafficcontrol.hxx>
 #include "parking.hxx"
 #include "groundnetwork.hxx"
 #include "runwayprefs.hxx"
-#include "trafficcontrol.hxx"
+
+//typedef vector<float> DoubleVec;
+//typedef vector<float>::iterator DoubleVecIterator;
 
 class FGAirport;
 
+
 class FGAirportDynamics {
 
 private:
   FGAirport* _ap;
 
-  FGParkingVec       parkings;
-  FGRunwayPreference rwyPrefs;
-  FGGroundNetwork    groundNetwork;
-  FGTowerController  towerController;
+  FGParkingVec        parkings;
+  FGRunwayPreference  rwyPrefs;
+  FGStartupController startupController;
+  FGGroundNetwork     groundNetwork;
+  FGTowerController   towerController;
 
   time_t lastUpdate;
   string prevTrafficType;
@@ -52,6 +57,12 @@ private:
   stringVec takeoff;
   stringVec milActive, comActive, genActive, ulActive;
   stringVec *currentlyActive;
+  intVec freqAwos;     // </AWOS>
+  intVec freqUnicom;   // </UNICOM>
+  intVec freqClearance;// </CLEARANCE>
+  intVec freqGround;   // </GROUND>
+  intVec freqTower;    // </TOWER>
+  intVec freqApproach; // </APPROACH>
 
   // Experimental keep a running average of wind dir and speed to prevent
   // Erratic runway changes. 
@@ -67,6 +78,12 @@ public:
   FGAirportDynamics(const FGAirportDynamics &other);
   ~FGAirportDynamics();
 
+  void addAwosFreq     (int val) { freqAwos.push_back(val);      };
+  void addUnicomFreq   (int val) { freqUnicom.push_back(val);    };
+  void addClearanceFreq(int val) { freqClearance.push_back(val); };
+  void addGroundFreq   (int val) { freqGround.push_back(val);    };
+  void addTowerFreq    (int val) { freqTower.push_back(val);     };
+  void addApproachFreq (int val) { freqApproach.push_back(val);  };
 
   void init();
   double getLongitude() const; 
@@ -90,9 +107,12 @@ public:
   //FGAirport *getAddress() { return this; };
   //const string &getName() const { return _name;};
   // Returns degrees
+  int getGroundFrequency() { return freqGround.size() ? freqGround[0] : 0; };
+
+  FGStartupController *getStartupController() { return &startupController; };
+  FGGroundNetwork     *getGroundNetwork()     { return &groundNetwork; };
+  FGTowerController   *getTowerController()   { return &towerController; };
 
-  FGGroundNetwork   *getGroundNetwork()   { return &groundNetwork; };
-  FGTowerController *getTowerController() { return &towerController; };
   
 
   void setRwyUse(const FGRunwayPreference& ref);
index 05f786a20d135f1e86dfeba5f19aef4b8e7009a3..1f2f0fcab452c61419303efd0872a027a8f3349d 100644 (file)
@@ -476,189 +476,12 @@ int FGTaxiSegment::getPenalty(int nGates) {
      return penalty;
 }
 
-// void FGGroundNetwork::trace(FGTaxiNode *currNode, int end, int depth, double distance)
-// {
-//   // Just check some preconditions of the trace algorithm
-//   if (nodesStack.size() != routesStack.size()) 
-//     {
-//       SG_LOG(SG_GENERAL, SG_ALERT, "size of nodesStack and routesStack is not equal. NodesStack :" 
-//          << nodesStack.size() << ". RoutesStack : " << routesStack.size());
-//     }
-//   nodesStack.push_back(currNode->getIndex());
-//   totalDistance += distance;
-//   //cerr << "Starting trace " << currNode->getIndex() << " " << "total distance: " << totalDistance << endl;
-//   //     << currNode->getIndex() << endl;
-// 
-//   // If the current route matches the required end point we found a valid route
-//   // So we can add this to the routing table
-//   if (currNode->getIndex() == end)
-//     {
-//       maxDepth = depth;
-//       //cerr << "Found route : " <<  totalDistance << "" << " " << *(nodesStack.end()-1) << " Depth = " << depth << endl;
-//       routes.push_back(FGTaxiRoute(nodesStack,routesStack,totalDistance, depth));
-//       if (nodesStack.empty() || routesStack.empty())
-//     {
-//       printRoutingError(string("while finishing route"));
-//     }
-//       nodesStack.pop_back();
-//       routesStack.pop_back();
-//       if (!(foundRoute)) {
-//     maxDistance = totalDistance;
-//       }
-//       else
-//     if (totalDistance < maxDistance)
-//       maxDistance = totalDistance;
-//       foundRoute = true;
-//       totalDistance -= distance;
-//       return;
-//     }
-//  
-// 
-//   // search if the currentNode has been encountered before
-//   // if so, we should step back one level, because it is
-//   // rather rediculous to proceed further from here. 
-//   // if the current node has not been encountered before,
-//   // i should point to nodesStack.end()-1; and we can continue
-//   // if i is not nodesStack.end, the previous node was found, 
-//   // and we should return. 
-//   // This only works at trace levels of 1 or higher though
-//   if (depth > 0) {
-//     intVecIterator i = nodesStack.begin();
-//     while ((*i) != currNode->getIndex()) {
-//       //cerr << "Route so far : " << (*i) << endl;
-//       i++;
-//     }
-//     if (i != nodesStack.end()-1) {
-//       if (nodesStack.empty() || routesStack.empty())
-//     {
-//       printRoutingError(string("while returning from an already encountered node"));
-//     }
-//       nodesStack.pop_back();
-//       routesStack.pop_back();
-//       totalDistance -= distance;
-//       return;
-//     }
-//     if (depth >= maxDepth) {
-//       count++;
-//       if (!(count % 100000)) {
-//     maxDepth--; // Gradually decrease maxdepth, to prevent "eternal searches"
-//     //cerr << "Reducing maxdepth to " << maxDepth << endl;
-//       }
-//       nodesStack.pop_back();
-//       routesStack.pop_back();
-//       totalDistance -= distance;
-//       return;
-//     }
-//     // If the total distance from start to the current waypoint
-//     // is longer than that of a route we can also stop this trace 
-//     // and go back one level. 
-//     if ((totalDistance > maxDistance) && foundRoute)
-//       //if (foundRoute)
-//       {
-//     //cerr << "Stopping rediculously long trace: " << totalDistance << endl;
-//     if (nodesStack.empty() || routesStack.empty())
-//     {
-//       printRoutingError(string("while returning from finding a rediculously long route"));
-//     }
-//     nodesStack.pop_back();
-//     routesStack.pop_back();
-//     totalDistance -= distance;
-//     return;
-//       }
-//   }
-/*  
-  //cerr << "2" << endl;
-  if (currNode->getBeginRoute() != currNode->getEndRoute())
-    {
-      double course, length;
-      //cerr << "3" << endl;
-      // calculate distance and heading "as the crow flies" between starn and end points"
-      SGWayPoint first(currNode->getLongitude(),
-                      currNode->getLatitude(),
-                      0);
-      //SGWayPoint second (lastNode->getLongitude(),
-      //                lastNode->getLatitude(),
-      //                    0);
-  
-      first.CourseAndDistance(destination, &course, &length);
-      //for (FGTaxiSegmentVectorIterator 
-      //            itr = segments.begin();
-      //          itr != segments.end(); itr++)
-      //       {
-      //         (*itr)->setCourseDiff(course);
-      //       } 
-      //FGTaxiNodeVectorIterator nde = nodes.begin();
-      //while (nde != nodes.end()) {
-      //(*nde)->sortEndSegments();
-      //nde++;
-      
-      for (FGTaxiSegmentVectorIterator 
-            i = currNode->getBeginRoute();
-          i != currNode->getEndRoute();
-          i++)
-       {
-         (*i)->setCourseDiff(course);
-       }
-      currNode->sortEndSegments(foundRoute);
-      for (FGTaxiSegmentVectorIterator 
-            i = currNode->getBeginRoute();
-          i != currNode->getEndRoute();
-          i++)
-       {
-         //cerr << (*i)->getLength() << endl;
-         //cerr << (*i)->getIndex() << endl;
-         int idx = (*i)->getIndex();
-         routesStack.push_back((*i)->getIndex());
-         trace((*i)->getEnd(), end, depth+1, (*i)->getLength());
-       //  {
-       //      // cerr << currNode -> getIndex() << " ";
-       //      route.push_back(currNode->getIndex());
-       //      return true;
-       //    }
-       }
-    }
-  else
-    {
-      //SG_LOG( SG_GENERAL, SG_DEBUG, "4" );
-    }
-  if (nodesStack.empty())
-    {
-      printRoutingError(string("while finishing trace"));
-    }
-  nodesStack.pop_back();
-  // Make sure not to dump the level-zero routesStack entry, because that was never created.
-  if (depth)
-    {
-      routesStack.pop_back();
-      //cerr << "leaving trace " << routesStack.size() << endl;
-    }
-  totalDistance -= distance;
-  return;
-}*/
-/*
-void FGGroundNetwork::printRoutingError(string mess)
-{
-  SG_LOG(SG_GENERAL, SG_ALERT,  "Error in ground network trace algorithm " << mess);
-  if (nodesStack.empty())
-    {
-      SG_LOG(SG_GENERAL, SG_ALERT, " nodesStack is empty. Dumping routesStack");
-      for (intVecIterator i = routesStack.begin() ; i != routesStack.end(); i++)
-       SG_LOG(SG_GENERAL, SG_ALERT, "Route " << (*i));
-    }
-  if (routesStack.empty())
-    {
-      SG_LOG(SG_GENERAL, SG_ALERT, " routesStack is empty. Dumping nodesStack"); 
-      for (intVecIterator i = nodesStack.begin() ; i != nodesStack.end(); i++)
-       SG_LOG(SG_GENERAL, SG_ALERT, "Node " << (*i));
-    }
-  //exit(1);
-}
-*/
+/* ATC Related Functions */
 
 void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
                                       double lat, double lon, double heading, 
                                       double speed, double alt, double radius, int leg,
-                                      string callsign)
+                                      FGAIAircraft *aircraft)
 {
    TrafficVectorIterator i = activeTraffic.begin();
    // Search search if the current id alread has an entry
@@ -679,7 +502,7 @@ void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, in
      rec.setPositionAndIntentions(currentPosition, intendedRoute);
      rec.setPositionAndHeading(lat, lon, heading, speed, alt);
      rec.setRadius(radius); // only need to do this when creating the record.
-     rec.setCallSign(callsign);
+     rec.setAircraft(aircraft);
      activeTraffic.push_back(rec);
    } else {
      i->setPositionAndIntentions(currentPosition, intendedRoute); 
index f27aff980b9d6db20c7cc4bfcbea35566799d5c7..33adb7a138b76610910fdc3ad6d5a80a2d9332af 100644 (file)
@@ -35,7 +35,7 @@ SG_USING_STD(vector);
 
 #include "gnnode.hxx"
 #include "parking.hxx"
-#include "trafficcontrol.hxx"
+#include <ATC/trafficcontrol.hxx>
 
 class FGTaxiSegment; // forward reference
 class FGAIFlightPlan; // forward reference
@@ -265,7 +265,7 @@ public:
 
   virtual void announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute, 
                                double lat, double lon, double hdg, double spd, double alt, 
-                               double radius, int leg, string callsign);
+                               double radius, int leg, FGAIAircraft *aircraft);
   virtual void signOff(int id);
   virtual void update(int id, double lat, double lon, double heading, double speed, double alt, double dt);
   virtual bool hasInstruction(int id);
index 3b50d5d2a8a308daa4180ff96519f947b078c959..908acf0294b3e597d458323dda0b1f3234ee80d5 100644 (file)
@@ -58,11 +58,8 @@ public:
   FGParking() :
       heading(0),
       radius(0),
-      //parkingName(0),
-      //type(0),
-      //airlineCodes(0),
       available(true),
-      pushBackPoint(-1),
+      pushBackPoint(0),
       pushBackRoute(0)
   {
   };
index 48a0d6ed461e133d7e521e441ce7e76bacb2f5ef..a6112e44e569252c23bd641d38b2bda4a7df8b9b 100644 (file)
@@ -118,7 +118,7 @@ string ScheduleTime::getName(time_t dayStart)
       //couldn't find one so return 0;
       //cerr << "Returning 0 " << endl;
     }
-    return string(0);
+    return string("");
 }                            
 /******************************************************************************
  * RunwayList
index 9be3ca3746d62a5ef4a014379286bacafbb25be2..55376f2bb10bc4731c3da5fddfa98aed729f9deb 100644 (file)
@@ -70,7 +70,7 @@ fgfs_SOURCES = bootstrap.cxx
 fgfs_LDADD = \
        $(top_builddir)/src/Main/libMain.a \
        $(top_builddir)/src/Aircraft/libAircraft.a \
-       $(top_builddir)/src/ATCDCL/libATC.a \
+       $(top_builddir)/src/ATCDCL/libATCDCL.a \
        $(top_builddir)/src/Cockpit/libCockpit.a \
        $(top_builddir)/src/Cockpit/built_in/libBuilt_in.a \
        $(top_builddir)/src/FDM/libFlight.a \
@@ -97,6 +97,7 @@ fgfs_LDADD = \
        $(top_builddir)/src/Airports/libAirports.a \
        $(MPLAYER_LIBS) \
         $(top_builddir)/src/AIModel/libAIModel.a \
+       $(top_builddir)/src/ATC/libATC.a \
        $(top_builddir)/src/Systems/libSystems.a \
        $(top_builddir)/src/Time/libTime.a \
        $(top_builddir)/src/Traffic/libTraffic.a \
index af9ce9c7666f73d00ef9e6e74ff80cb1028ef33d..178419eb064efa6da3bc520000cf51b19c288cd3 100644 (file)
@@ -2,6 +2,7 @@ SUBDIRS = \
         Include \
         Aircraft \
         Airports \
+       ATC \
         ATCDCL \
         Autopilot \
         Cockpit \