]> git.mxchange.org Git - flightgear.git/blobdiff - src/ATC/AIMgr.cxx
Lots of changes to the ATC/AI system for initial revision of random AI GA VFR traffic
[flightgear.git] / src / ATC / AIMgr.cxx
index 871feb8b461f58ed6ef5511c85470c2179c8d471..7c9e60350241357f74c33c6ab6af56742372814e 100644 (file)
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-#include <Main/fgfs.hxx>
+#include <simgear/misc/sg_path.hxx>
+
 #include <Main/fg_props.hxx>
 #include <Main/globals.hxx>
-#include <simgear/misc/sg_path.hxx>
+#include <simgear/math/sg_random.h>
 
 #include <list>
 
 #  include <dirent.h>          // for directory reading
 #endif
 
+#ifdef FG_WEATHERCM
+# include <WeatherCM/FGLocalWeatherDatabase.h>
+#else
+# include <Environment/environment_mgr.hxx>
+# include <Environment/environment.hxx>
+#endif
+
 #include "AIMgr.hxx"
 #include "AILocalTraffic.hxx"
+#include "AIGAVFRTraffic.hxx"
 #include "ATCutils.hxx"
+#include "commlist.hxx"
 
 SG_USING_STD(list);
 SG_USING_STD(cout);
@@ -43,12 +53,18 @@ SG_USING_STD(cout);
 FGAIMgr::FGAIMgr() {
        ATC = globals->get_ATC_mgr();
        initDone = false;
+       ai_callsigns_used["CFGFS"] = 1; // so we don't inadvertently use this
+       // TODO - use the proper user callsign when it becomes user settable.
+       removalList.clear();
+       activated.clear();
 }
 
 FGAIMgr::~FGAIMgr() {
 }
 
 void FGAIMgr::init() {
+       //cout << "AIMgr::init called..." << endl;
+       
        // Pointers to user's position
        lon_node = fgGetNode("/position/longitude-deg", true);
        lat_node = fgGetNode("/position/latitude-deg", true);
@@ -58,12 +74,26 @@ void FGAIMgr::init() {
        lat = lat_node->getDoubleValue();
        elev = elev_node->getDoubleValue();
        
+       // Load up models at the start to avoid pausing later
+       // Hack alert - Hardwired paths!!
+       string planepath = "Aircraft/c172/Models/c172-dpm.ac";
+       _defaultModel = sgLoad3DModel( globals->get_fg_root(),
+                                         planepath.c_str(),
+                                         globals->get_props(),
+                                         globals->get_sim_time_sec() );
+                                                                         
+       planepath = "Aircraft/pa28-161/Models/pa28-161.ac";
+       _piperModel = sgLoad3DModel( globals->get_fg_root(),
+                                         planepath.c_str(),
+                                         globals->get_props(),
+                                         globals->get_sim_time_sec() );
+                                                                         
        // go through the $FG_ROOT/ATC directory and find all *.taxi files
        SGPath path(globals->get_fg_root());
        path.append("ATC/");
        string dir = path.dir();
-    string ext;
-    string file, f_ident;
+       string ext;
+       string file, f_ident;
        int pos;
        
        // WARNING - I (DCL) haven't tested this on MSVC - this is simply cribbed from TerraGear
@@ -75,7 +105,7 @@ void FGAIMgr::init() {
        path_str = dir + "\\*.*";
        
        if ( ( hfile = _findfirst( path.c_str(), &de ) ) == -1 ) {
-               cout << "cannot open directory " << dir << "\n";
+               SG_LOG(SG_ATC, SG_WARN, "cannot open directory " << dir);
        } else {                
                // load all .taxi files
                do {
@@ -83,20 +113,19 @@ void FGAIMgr::init() {
                        pos = file.find(".");
                        ext = file.substr(pos + 1);
                        if(ext == "taxi") {
-                               cout << "TAXI FILE FOUND!!!\n";
                                f_ident = file.substr(0, pos);
                                FGAirport a;
                                if(dclFindAirportID(f_ident, &a)) {
                                        SGBucket sgb(a.longitude, a.latitude);
                                        int idx = sgb.gen_index();
-                                       if(airports.find(idx) != airports.end()) {
-                                               airports[idx]->push_back(f_ident);
+                                       if(facilities.find(idx) != facilities.end()) {
+                                               facilities[idx]->push_back(f_ident);
                                        } else {
                                                aptID_list_type* apts = new aptID_list_type;
                                                apts->push_back(f_ident);
-                                               airports[idx] = apts;
+                                               facilities[idx] = apts;
                                        }
-                                       cout << "Mapping " << f_ident << " to bucket " << idx << '\n'
+                                       SG_LOG(SG_ATC, SG_BULK, "Mapping " << f_ident << " to bucket " << idx)
                                }
                        }
                } while ( _findnext( hfile, &de ) == 0 );
@@ -107,32 +136,27 @@ void FGAIMgr::init() {
     struct dirent *de;
 
     if ( (d = opendir( dir.c_str() )) == NULL ) {
-               cout << "cannot open directory " << dir << "\n";
+               SG_LOG(SG_ATC, SG_WARN, "cannot open directory " << dir);
        } else {
-               cout << "Opened directory " << dir << " OK :-)\n";
-               cout << "Contents are:\n";
                // load all .taxi files
                while ( (de = readdir(d)) != NULL ) {
                        file = de->d_name;
                        pos = file.find(".");
-                       cout << file << '\n';
-
                        ext = file.substr(pos + 1);
                        if(ext == "taxi") {
-                               cout << "TAXI FILE FOUND!!!\n";
                                f_ident = file.substr(0, pos);
                                FGAirport a;
                                if(dclFindAirportID(f_ident, &a)) {
                                        SGBucket sgb(a.longitude, a.latitude);
                                        int idx = sgb.gen_index();
-                                       if(airports.find(idx) != airports.end()) {
-                                               airports[idx]->push_back(f_ident);
+                                       if(facilities.find(idx) != facilities.end()) {
+                                               facilities[idx]->push_back(f_ident);
                                        } else {
-                                               aptID_list_type* apts = new aptID_list_type;
+                                               ID_list_type* apts = new ID_list_type;
                                                apts->push_back(f_ident);
-                                               airports[idx] = apts;
+                                               facilities[idx] = apts;
                                        }
-                                       cout << "Mapping " << f_ident << " to bucket " << idx << '\n'; 
+                                       SG_LOG(SG_ATC, SG_BULK, "Mapping " << f_ident << " to bucket " << idx);
                                }
                        }
                }               
@@ -141,9 +165,27 @@ void FGAIMgr::init() {
 #endif
        
        // See if are in range at startup and activate if necessary
-       SearchByPos(10.0);
+       SearchByPos(15.0);
        
        initDone = true;
+       
+       //cout << "AIMgr::init done..." << endl;
+       
+       /*
+       // TESTING
+       FGATCAlignedProjection ortho;
+       ortho.Init(dclGetAirportPos("KEMT"), 205.0);    // Guess of rwy19 heading
+       //Point3D ip = ortho.ConvertFromLocal(Point3D(6000, 1000, 1000));       // 90 deg entry
+       //Point3D ip = ortho.ConvertFromLocal(Point3D(-7000, 3000, 1000));      // 45 deg entry
+       Point3D ip = ortho.ConvertFromLocal(Point3D(1000, -7000, 1000));        // straight-in
+       ATC->AIRegisterAirport("KEMT");
+       FGAIGAVFRTraffic* p = new FGAIGAVFRTraffic();
+       p->SetModel(_defaultModel);
+       p->Init(ip, "KEMT", GenerateShortForm(GenerateUniqueCallsign()));
+       ai_list.push_back(p);
+       traffic[ident].push_back(p);
+       activated["KEMT"] = 1;
+       */      
 }
 
 void FGAIMgr::bind() {
@@ -158,6 +200,11 @@ void FGAIMgr::update(double dt) {
                SG_LOG(SG_ATC, SG_WARN, "Warning - AIMgr::update(...) called before AIMgr::init()");
        }
        
+       //cout << activated.size() << '\n';
+       
+       Point3D userPos = Point3D(lon_node->getDoubleValue(), lat_node->getDoubleValue(), elev_node->getDoubleValue());
+       
+       // TODO - make these class variables!!
        static int i = 0;
        static int j = 0;
 
@@ -169,25 +216,106 @@ void FGAIMgr::update(double dt) {
        }
        
        if(j == 215) {
-               SearchByPos(15.0);
+               SearchByPos(25.0);
                j = 0;
+       } else if(j == 200) {
+               // Go through the list of activated airports and remove those out of range
+               //cout << "The following airports have been activated by the AI system:\n";
+               ai_activated_map_iterator apt_itr = activated.begin();
+               while(apt_itr != activated.end()) {
+                       //cout << "FIRST IS " << (*apt_itr).first << '\n';
+                       if(dclGetHorizontalSeparation(userPos, dclGetAirportPos((*apt_itr).first)) > (35.0 * 1600.0)) {
+                               // Then get rid of it and make sure the iterator is left pointing to the next one!
+                               string s = (*apt_itr).first;
+                               if(traffic.find(s) != traffic.end()) {
+                                       //cout << "s = " << s << ", traffic[s].size() = " << traffic[s].size() << '\n';
+                                       if(traffic[s].size()) {
+                                               apt_itr++;
+                                       } else {
+                                               //cout << "Erasing " << (*apt_itr).first << " and traffic" << '\n';
+                                               activated.erase(apt_itr++);
+                                               traffic.erase(s);
+                                       }
+                               } else {
+                                               //cout << "Erasing " << (*apt_itr).first << ' ' << (*apt_itr).second << '\n';
+                                               activated.erase(apt_itr++);
+                               }
+                       } else {
+                               apt_itr++;
+                       }
+               }
+       } else if(j == 180) {
+               // Go through the list of activated airports and do the random airplane generation
+               ai_traffic_map_iterator it = traffic.begin();
+               while(it != traffic.end()) {
+                       string s = (*it).first;
+                       //cout << "s = " << s << " size = " << (*it).second.size() << '\n';
+                       // Only generate extra traffic if within a certain distance of the user,
+                       // TODO - maybe take users's tuned freq into account as well.
+                       double d = dclGetHorizontalSeparation(userPos, dclGetAirportPos(s)); 
+                       if(d < (15.0 * 1600.0)) {
+                               double cd = 0.0;
+                               bool gen = false;
+                               //cout << "Size of list is " << (*it).second.size() << " at " << s << '\n';
+                               if((*it).second.size()) {
+                                       FGAIEntity* e = *((*it).second.rbegin());
+                                       cd = dclGetHorizontalSeparation(e->GetPos(), dclGetAirportPos(s));
+                                       if(cd < (d < 5000 ? 10000 : d + 5000)) {
+                                               gen = true;
+                                       }
+                               } else {
+                                       gen = true;
+                                       cd = 0.0;
+                               }
+                               if(gen) {
+                                       //cout << "Generating extra traffic at airport " << s << ", at least " << cd << " meters out\n";
+                                       //GenerateSimpleAirportTraffic(s, cd);
+                                       GenerateSimpleAirportTraffic(s, cd + 2000.0);   // The random seems a bit wierd - traffic could get far too bunched without the +2000.
+                               }
+                       }
+                       ++it;
+               }
        }
        
        ++j;
        
+       //cout << "Size of AI list is " << ai_list.size() << '\n';
+       
        // TODO - need to add a check of if any activated airports have gone out of range
        
+       string rs;      // plane to be removed, if one.
+       if(removalList.size()) {
+               rs = *(removalList.begin());
+               removalList.pop_front();
+       } else {
+               rs = "";
+       }
+       
        // Traverse the list of active planes and run all their update methods
        // TODO - spread the load - not all planes should need updating every frame.
        // Note that this will require dt to be calculated for each plane though
        // since they rely on it to calculate distance travelled.
        ai_list_itr = ai_list.begin();
        while(ai_list_itr != ai_list.end()) {
-               (*ai_list_itr)->Update(dt);
-               ++ai_list_itr;
+               FGAIEntity *e = *ai_list_itr;
+               if(rs.size() && e->GetCallsign() == rs) {
+                       //cout << "Removing " << rs << " from ai_list\n";
+                       ai_list_itr = ai_list.erase(ai_list_itr);
+                       delete e;
+                       // This is a hack - we should deref this plane from the airport count!
+               } else {
+                       e->Update(dt);
+                       ++ai_list_itr;
+               }
        }
+
+       //cout << "Size of AI list is " << ai_list.size() << '\n';
 }
 
+void FGAIMgr::ScheduleRemoval(string s) {
+       //cout << "Scheduling removal of plane " << s << " from AIMgr\n";
+       removalList.push_back(s);
+}
 
 // Activate AI traffic at an airport
 void FGAIMgr::ActivateAirport(string ident) {
@@ -195,21 +323,197 @@ void FGAIMgr::ActivateAirport(string ident) {
        // TODO - need to start the traffic more randomly
        FGAILocalTraffic* local_traffic = new FGAILocalTraffic;
        //local_traffic->Init(ident, IN_PATTERN, TAKEOFF_ROLL);
-       local_traffic->Init(ident);
+       local_traffic->Init(GenerateShortForm(GenerateUniqueCallsign()), ident);
        local_traffic->FlyCircuits(1, true);    // Fly 2 circuits with touch & go in between
        ai_list.push_back(local_traffic);
+       traffic[ident].push_back(local_traffic);
+       //cout << "******** ACTIVATING AIRPORT, ident = " << ident << '\n';
+       activated[ident] = 1;
+}
+
+// Hack - Generate AI traffic at an airport with no facilities file
+void FGAIMgr::GenerateSimpleAirportTraffic(string ident, double min_dist) {
+       // Ugly hack - don't let VFR Cessnas operate at a hardwired list of major airports
+       // This will go eventually once airport .xml files specify the traffic profile
+       if(ident == "KSFO" || ident == "KDFW" || ident == "EGLL" || ident == "KORD" || ident == "KJFK" 
+                          || ident == "KMSP" || ident == "KLAX" || ident == "KBOS" || ident == "KEDW"
+                                          || ident == "KSEA" || ident == "EHAM") {
+               return;
+       }
+       
+       /*
+       // TODO - check for military airports - this should be in the current data.
+       // UGGH - there's no point at the moment - everything is labelled civil in basic.dat!
+       FGAirport a;
+       if(dclFindAirportID(ident, &a)) {
+               cout << "CODE IS " << a.code << '\n';
+       } else {
+               // UG - can't find the airport!
+               return;
+       }
+       */
+       
+       Point3D aptpos = dclGetAirportPos(ident);       // TODO - check for elev of -9999
+       //cout << "ident = " << ident << ", elev = " << aptpos.elev() << '\n';
+       
+       // Operate from airports at 3000ft and below only to avoid the default cloud layers and since we don't degrade AI performance with altitude.
+       if(aptpos.elev() > 3000) {
+               //cout << "High alt airports not yet supported - returning\n";
+               return;
+       }
+       
+       // Rough hack for plane type - make 70% of the planes cessnas, the rest pipers.
+       bool cessna = true;
+       
+       // Get the time and only operate VFR in the (approximate) daytime.
+       //SGTime *t = globals->get_time_params();
+       string time_str = fgGetString("sim/time/gmt-string");
+       int loc_time = atoi((time_str.substr(0,3)).c_str());
+       //cout << "gmt_time = " << loc_time << '\n';
+       loc_time += (int)((aptpos.lon() / 360.0) * 24.0);
+       while(loc_time < 0) loc_time += 24;
+       while(loc_time > 24) loc_time -= 24;
+       //cout << "loc_time = " << loc_time << '\n';
+       if(loc_time < 7 || loc_time > 19) return;
+       
+       // Check that the visibility is OK for IFR operation.
+       double visibility;
+       #ifdef FG_WEATHERCM
+       //sgVec3 position = { aptpos.lat(), aptpos.lon(), aptpos.elev() };
+       //FGPhysicalProperty stationweather = WeatherDatabase->get(position);
+       #else
+       FGEnvironment stationweather =
+            ((FGEnvironmentMgr *)globals->get_subsystem("environment"))
+              ->getEnvironment(aptpos.lat(), aptpos.lon(), aptpos.elev());     // TODO - check whether this should take ft or m for elev.
+       #endif
+       #ifdef FG_WEATHERCM
+       visibility = fgGetDouble("/environment/visibility-m");
+       #else
+       visibility = stationweather.get_visibility_m();
+       #endif
+       // Technically we can do VFR down to 1 mile (1600m) but that's pretty murky!
+       //cout << "vis = " << visibility << '\n';
+       if(visibility < 3000) return;
+       
+       ATC->AIRegisterAirport(ident);
+       
+       // Next - get the distance from user to the airport.
+       Point3D userpos = Point3D(lon_node->getDoubleValue(), lat_node->getDoubleValue(), elev_node->getDoubleValue());
+       double d = dclGetHorizontalSeparation(userpos, aptpos); // in meters
+       
+       int lev = fgGetInt("/sim/ai-traffic/level");
+       if(lev < 1 || lev > 3) lev = 2;
+       if(visibility < 6000) lev = 1;
+       //cout << "level = " << lev << '\n';
+       
+       // Next - generate any local / circuit traffic
+
+       /*
+       // --------------------------- THIS BLOCK IS JUST FOR TESTING - COMMENT OUT BEFORE RELEASE ---------------
+       // Finally - generate VFR approaching traffic
+       //if(d > 2000) {
+       if(ident == "KPOC") {
+               double ad = 2000.0;
+               double avd = 3000.0;    // average spacing of arriving traffic in meters - relate to airport business and AI density setting one day!
+               //while(ad < (d < 10000 ? 12000 : d + 2000)) {
+               for(int i=0; i<8; ++i) {
+                       double dd = sg_random() * avd;
+                       // put a minimum spacing in for now since I don't think tower will cope otherwise!
+                       if(dd < 1500) dd = 1500; 
+                       //ad += dd;
+                       ad += dd;
+                       double dir = int(sg_random() * 36);
+                       if(dir == 36) dir--;
+                       dir *= 10;
+                       //dir = 180;
+                       if(sg_random() < 0.3) cessna = false;
+                       else cessna = true;
+                       string s = GenerateShortForm(GenerateUniqueCallsign(), (cessna ? "Cessna-" : "Piper-"));
+                       FGAIGAVFRTraffic* t = new FGAIGAVFRTraffic();
+                       t->SetModel(cessna ? _defaultModel : _piperModel);
+                       //cout << "Generating VFR traffic " << s << " inbound to " << ident << " " << ad << " meters out from " << dir << " degrees\n";
+                       Point3D tpos = dclUpdatePosition(aptpos, dir, 6.0, ad);
+                       if(tpos.elev() > (aptpos.elev() + 3000.0)) tpos.setelev(aptpos.elev() + 3000.0);
+                       t->Init(tpos, ident, s);
+                       ai_list.push_back(t);
+               }
+       }
        activated[ident] = 1;
-}      
+       return;
+       //---------------------------------------------------------------------------------------------------
+       */
+       
+       double ad;   // Minimum distance out of first arriving plane in meters.
+       double mind; // Minimum spacing of traffic in meters
+       double avd;  // average spacing of arriving traffic in meters - relate to airport business and AI density setting one day!
+       // Finally - generate VFR approaching traffic
+       //if(d > 2000) {
+       if(1) {
+               if(lev == 3) {
+                       ad = 5000.0;
+                       mind = 2000.0;
+                       avd = 6000.0;
+               } else if(lev == 2) {
+                       ad = 8000.0;
+                       mind = 4000.0;
+                       avd = 10000.0;
+               } else {
+                       ad = 9000.0;    // Start the first aircraft at least 9K out for now.
+                       mind = 6000.0;
+                       avd = 15000.0;
+               }
+               /*
+               // Check if there is already arriving traffic at this airport
+               cout << "BING A " << ident << '\n';
+               if(traffic.find(ident) != traffic.end()) {
+                       cout << "BING B " << ident << '\n';
+                       ai_list_type lst = traffic[ident];
+                       cout << "BING C " << ident << '\n';
+                       if(lst.size()) {
+                               cout << "BING D " << ident << '\n';
+                               double cd = dclGetHorizontalSeparation(aptpos, (*lst.rbegin())->GetPos());
+                               cout << "ident = " << ident << ", cd = " << cd << '\n';
+                               if(cd > ad) ad = cd;
+                       }
+               }
+               */
+               if(min_dist != 0) ad = min_dist;
+               //cout << "ident = " << ident << ", ad = " << ad << '\n';
+               while(ad < (d < 5000 ? 15000 : d + 10000)) {
+                       double dd = mind + (sg_random() * (avd - mind));
+                       ad += dd;
+                       double dir = int(sg_random() * 36);
+                       if(dir == 36) dir--;
+                       dir *= 10;
+                       if(sg_random() < 0.3) cessna = false;
+                       else cessna = true;
+                       string s = GenerateShortForm(GenerateUniqueCallsign(), (cessna ? "Cessna-" : "Piper-"));
+                       FGAIGAVFRTraffic* t = new FGAIGAVFRTraffic();
+                       t->SetModel(cessna ? _defaultModel : _piperModel);
+                       //cout << "Generating VFR traffic " << s << " inbound to " << ident << " " << ad << " meters out from " << dir << " degrees\n";
+                       Point3D tpos = dclUpdatePosition(aptpos, dir, 6.0, ad);
+                       if(tpos.elev() > (aptpos.elev() + 3000.0)) tpos.setelev(aptpos.elev() + 3000.0);        // FEET yuk :-(
+                       t->Init(tpos, ident, s);
+                       ai_list.push_back(t);
+                       traffic[ident].push_back(t);
+               }
+       }       
+}
 
+/*
+// Generate a VFR arrival at airport apt, at least distance d (meters) out.
+void FGAIMgr::GenerateVFRArrival(string apt, double d) {
+}
+*/
 
 // Search for valid airports in the vicinity of the user and activate them if necessary
-void FGAIMgr::SearchByPos(double range)
-{
+void FGAIMgr::SearchByPos(double range) {
        //cout << "In SearchByPos(...)" << endl;
        
        // get bucket number for plane position
        lon = lon_node->getDoubleValue();
        lat = lat_node->getDoubleValue();
+       elev = elev_node->getDoubleValue() * SG_FEET_TO_METER;
        SGBucket buck(lon, lat);
 
        // get neigboring buckets
@@ -218,6 +522,7 @@ void FGAIMgr::SearchByPos(double range)
        int by = (int)( range*SG_NM_TO_METER / buck.get_height_m() / 2 );
        //cout << "by = " << by << endl;
        
+       // Search for airports with facitities files --------------------------
        // loop over bucket range 
        for ( int i=-bx; i<=bx; i++) {
                //cout << "i loop\n";
@@ -226,10 +531,10 @@ void FGAIMgr::SearchByPos(double range)
                        buck = sgBucketOffset(lon, lat, i, j);
                        long int bucket = buck.gen_index();
                        //cout << "bucket is " << bucket << endl;
-                       if(airports.find(bucket) != airports.end()) {
-                               aptID_list_type* apts = airports[bucket];
-                               aptID_list_iterator current = apts->begin();
-                               aptID_list_iterator last = apts->end();
+                       if(facilities.find(bucket) != facilities.end()) {
+                               ID_list_type* apts = facilities[bucket];
+                               ID_list_iterator current = apts->begin();
+                               ID_list_iterator last = apts->end();
                                
                                //cout << "Size of apts is " << apts->size() << endl;
                                
@@ -245,7 +550,10 @@ void FGAIMgr::SearchByPos(double range)
                                                //if(dclFindAirportID(*current, &a)) {
                                                        //      // We can do something here based on distance from the user if we wish.
                                                //}
+                                               //string s = *current;
+                                               //cout << "s = " << s << '\n';
                                                ActivateAirport(*current);
+                                               //ActivateSimpleAirport(*current);      // TODO - put this back to ActivateAirport when that code is done.
                                                //cout << "Activation done" << endl;
                                        } else {
                                                //cout << *current << " already activated" << endl;
@@ -254,4 +562,86 @@ void FGAIMgr::SearchByPos(double range)
                        }
                }
        }
+       //-------------------------------------------------------------
+       
+       // Search for any towered airports in the vicinity ------------
+       comm_list_type towered;
+       comm_list_iterator twd_itr;
+       
+       int num_twd = current_commlist->FindByPos(lon, lat, elev, range, &towered, TOWER);
+       if (num_twd != 0) {
+               double closest = 1000000;
+               string s = "";
+               for(twd_itr = towered.begin(); twd_itr != towered.end(); twd_itr++) {
+                       // Only activate the closest airport not already activated each time.
+                       if(activated.find(twd_itr->ident) == activated.end()) {
+                               double sep = dclGetHorizontalSeparation(Point3D(lon, lat, elev), dclGetAirportPos(twd_itr->ident));
+                               if(sep < closest) {
+                                       closest = sep;
+                                       s = twd_itr->ident;
+                               }
+                               
+                       }
+               }
+               if(s.size()) {
+                       // TODO - find out why empty strings come through here when all in-range airports done.
+                       GenerateSimpleAirportTraffic(s);
+                       //cout << "**************ACTIVATING SIMPLE AIRPORT, ident = " << s << '\n';
+                       activated[s] = 1;
+               }
+       }
+}
+
+string FGAIMgr::GenerateCallsign() {
+       // For now we'll just generate US callsigns until we can regionally identify airports.
+       string s = "N";
+       // Add 3 to 5 numbers and make up to 5 with letters.
+       //sg_srandom_time();
+       double d = sg_random();
+       int n = int(d * 3);
+       if(n == 3) --n;
+       //cout << "First n, n = " << n << '\n';
+       int j = 3 + n;
+       //cout << "j = " << j << '\n';
+       for(int i=0; i<j; ++i) { 
+               int n = int(sg_random() * 10);
+               if(n == 10) --n;
+               s += (char)('0' + n);
+       }
+       for(int i=j; i<5; ++i) {
+               int n = int(sg_random() * 26);
+               if(n == 26) --n;
+               //cout << "Alpha, n = " << n << '\n';
+               s += (char)('A' + n);
+       }
+       //cout << "s = " << s << '\n';
+       return(s);
+}
+
+string FGAIMgr::GenerateUniqueCallsign() {
+       while(1) {
+               string s = GenerateCallsign();
+               if(!ai_callsigns_used[s]) {
+                       ai_callsigns_used[s] = 1;
+                       return(s);
+               }
+       }
+}
+
+// This will be moved somewhere else eventually!!!!
+string FGAIMgr::GenerateShortForm(string callsign, string plane_str, bool local) {
+       //cout << callsign << '\n';
+       string s;
+       if(local) s = "Trainer-";
+       else s = plane_str;
+       for(int i=3; i>0; --i) {
+               char c = callsign[callsign.size() - i];
+               //cout << c << '\n';
+               string tmp = "";
+               tmp += c;
+               if(isalpha(c)) s += GetPhoneticIdent(c);
+               else s += ConvertNumToSpokenDigits(tmp);
+               if(i > 1) s += '-';
+       }
+       return(s);
 }