]> git.mxchange.org Git - flightgear.git/blobdiff - src/ATC/AIMgr.cxx
any wind < 1kt is "calm", not just 0.0
[flightgear.git] / src / ATC / AIMgr.cxx
index 7c9e60350241357f74c33c6ab6af56742372814e..8b38de6639829aa11182c61f7cb1851c6a2368b5 100644 (file)
 //
 // 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-#include <simgear/misc/sg_path.hxx>
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
 
 #include <Main/fg_props.hxx>
 #include <Main/globals.hxx>
+#include <simgear/misc/sg_path.hxx>
 #include <simgear/math/sg_random.h>
-
 #include <list>
 
 #ifdef _MSC_VER
 #  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 <Environment/environment_mgr.hxx>
+#include <Environment/environment.hxx>
 
 #include "AIMgr.hxx"
 #include "AILocalTraffic.hxx"
@@ -53,13 +51,17 @@ 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
+       ai_callsigns_used["GFS"] = 1;   // so we don't inadvertently use this
        // TODO - use the proper user callsign when it becomes user settable.
        removalList.clear();
        activated.clear();
+       _havePiperModel = true;
 }
 
 FGAIMgr::~FGAIMgr() {
+    for (ai_list_itr = ai_list.begin(); ai_list_itr != ai_list.end(); ai_list_itr++) {
+        delete (*ai_list_itr);
+    }
 }
 
 void FGAIMgr::init() {
@@ -76,18 +78,37 @@ void FGAIMgr::init() {
        
        // 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() );
-                                                                         
+       string planepath = "Aircraft/c172p/Models/c172p.xml";
+       bool _loadedDefaultOK = true;
+       try {
+               _defaultModel = sgLoad3DModel( globals->get_fg_root(),
+                                          planepath.c_str(),
+                                                                          globals->get_props(),
+                                                                          globals->get_sim_time_sec() );
+       } catch(sg_exception&) {
+               _loadedDefaultOK = false;
+       }
+       
+       if(!_loadedDefaultOK ) {
+               // Just load the same 3D model as the default user plane - that's *bound* to exist!
+               // TODO - implement robust determination of availability of GA AI aircraft models
+               planepath = "Aircraft/c172p/Models/c172p.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() );
-                                                                         
+       try {
+               _piperModel = sgLoad3DModel( globals->get_fg_root(),
+                                        planepath.c_str(),
+                                                                        globals->get_props(),
+                                                                        globals->get_sim_time_sec() );
+       } catch(sg_exception&) {
+               _havePiperModel = false;
+       }
+
        // go through the $FG_ROOT/ATC directory and find all *.taxi files
        SGPath path(globals->get_fg_root());
        path.append("ATC/");
@@ -96,58 +117,22 @@ void FGAIMgr::init() {
        string file, f_ident;
        int pos;
        
-       // WARNING - I (DCL) haven't tested this on MSVC - this is simply cribbed from TerraGear
-#ifdef _MSC_VER        
-       long hfile;
-       struct _finddata_t de;
-       string path_str;
-       
-       path_str = dir + "\\*.*";
+       ulDir *d;
+       struct ulDirEnt *de;
        
-       if ( ( hfile = _findfirst( path.c_str(), &de ) ) == -1 ) {
-               SG_LOG(SG_ATC, SG_WARN, "cannot open directory " << dir);
-       } else {                
-               // load all .taxi files
-               do {
-                       file = de.name;
-                       pos = file.find(".");
-                       ext = file.substr(pos + 1);
-                       if(ext == "taxi") {
-                               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(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);
-                                               facilities[idx] = apts;
-                                       }
-                                       SG_LOG(SG_ATC, SG_BULK, "Mapping " << f_ident << " to bucket " << idx); 
-                               }
-                       }
-               } while ( _findnext( hfile, &de ) == 0 );
-       }
-#else
-
-    DIR *d;
-    struct dirent *de;
-
-    if ( (d = opendir( dir.c_str() )) == NULL ) {
+       if ( (d = ulOpenDir( dir.c_str() )) == NULL ) {
                SG_LOG(SG_ATC, SG_WARN, "cannot open directory " << dir);
        } else {
                // load all .taxi files
-               while ( (de = readdir(d)) != NULL ) {
+               while ( (de = ulReadDir(d)) != NULL ) {
                        file = de->d_name;
                        pos = file.find(".");
                        ext = file.substr(pos + 1);
                        if(ext == "taxi") {
                                f_ident = file.substr(0, pos);
-                               FGAirport a;
-                               if(dclFindAirportID(f_ident, &a)) {
-                                       SGBucket sgb(a.longitude, a.latitude);
+                               const FGAirport *a = fgFindAirportID( f_ident);
+                               if(a){
+                                       SGBucket sgb(a->getLongitude(), a->getLatitude());
                                        int idx = sgb.gen_index();
                                        if(facilities.find(idx) != facilities.end()) {
                                                facilities[idx]->push_back(f_ident);
@@ -160,10 +145,9 @@ void FGAIMgr::init() {
                                }
                        }
                }               
-               closedir(d);
+               ulCloseDir(d);
        }
-#endif
-       
+
        // See if are in range at startup and activate if necessary
        SearchByPos(15.0);
        
@@ -174,7 +158,7 @@ void FGAIMgr::init() {
        /*
        // TESTING
        FGATCAlignedProjection ortho;
-       ortho.Init(dclGetAirportPos("KEMT"), 205.0);    // Guess of rwy19 heading
+       ortho.Init(fgGetAirportPos("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
@@ -224,21 +208,23 @@ void FGAIMgr::update(double dt) {
                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)) {
+                       if(dclGetHorizontalSeparation(userPos, fgGetAirportPos((*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()) {
+                                       if(!traffic[s].empty()) {
                                                apt_itr++;
                                        } else {
                                                //cout << "Erasing " << (*apt_itr).first << " and traffic" << '\n';
-                                               activated.erase(apt_itr++);
+                                               activated.erase(apt_itr);
+                                               apt_itr = activated.upper_bound(s);
                                                traffic.erase(s);
                                        }
                                } else {
                                                //cout << "Erasing " << (*apt_itr).first << ' ' << (*apt_itr).second << '\n';
-                                               activated.erase(apt_itr++);
+                                               activated.erase(apt_itr);
+                                               apt_itr = activated.upper_bound(s);
                                }
                        } else {
                                apt_itr++;
@@ -252,14 +238,14 @@ void FGAIMgr::update(double dt) {
                        //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)); 
+                       double d = dclGetHorizontalSeparation(userPos, fgGetAirportPos(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));
+                                       FGAIEntity* e = *((*it).second.rbegin());       // Get the last airplane currently scheduled to arrive at this airport.
+                                       cd = dclGetHorizontalSeparation(e->GetPos(), fgGetAirportPos(s));
                                        if(cd < (d < 5000 ? 10000 : d + 5000)) {
                                                gen = true;
                                        }
@@ -270,7 +256,8 @@ void FGAIMgr::update(double dt) {
                                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.
+                                       GenerateSimpleAirportTraffic(s, cd + 3000.0);   // The random seems a bit wierd - traffic could get far too bunched without the +3000.
+                                       // TODO - make the anti-random constant variable depending on the ai-traffic level.
                                }
                        }
                        ++it;
@@ -312,16 +299,17 @@ void FGAIMgr::update(double dt) {
        //cout << "Size of AI list is " << ai_list.size() << '\n';
 }
 
-void FGAIMgr::ScheduleRemoval(string s) {
+void FGAIMgr::ScheduleRemoval(const 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) {
+void FGAIMgr::ActivateAirport(const string& ident) {
        ATC->AIRegisterAirport(ident);
        // TODO - need to start the traffic more randomly
        FGAILocalTraffic* local_traffic = new FGAILocalTraffic;
+       local_traffic->SetModel(_defaultModel.get());   // currently hardwired to cessna.
        //local_traffic->Init(ident, IN_PATTERN, TAKEOFF_ROLL);
        local_traffic->Init(GenerateShortForm(GenerateUniqueCallsign()), ident);
        local_traffic->FlyCircuits(1, true);    // Fly 2 circuits with touch & go in between
@@ -332,7 +320,7 @@ void FGAIMgr::ActivateAirport(string ident) {
 }
 
 // Hack - Generate AI traffic at an airport with no facilities file
-void FGAIMgr::GenerateSimpleAirportTraffic(string ident, double min_dist) {
+void FGAIMgr::GenerateSimpleAirportTraffic(const 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" 
@@ -344,8 +332,8 @@ void FGAIMgr::GenerateSimpleAirportTraffic(string ident, double min_dist) {
        /*
        // 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)) {
+       FGAirport a = fgFindAirportID(ident, &a);
+       if(a) {
                cout << "CODE IS " << a.code << '\n';
        } else {
                // UG - can't find the airport!
@@ -353,7 +341,7 @@ void FGAIMgr::GenerateSimpleAirportTraffic(string ident, double min_dist) {
        }
        */
        
-       Point3D aptpos = dclGetAirportPos(ident);       // TODO - check for elev of -9999
+       Point3D aptpos = fgGetAirportPos(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.
@@ -366,31 +354,22 @@ void FGAIMgr::GenerateSimpleAirportTraffic(string ident, double min_dist) {
        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;
+       struct tm *t = globals->get_time_params()->getGmt();
+       int loc_time = t->tm_hour + 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;
@@ -402,7 +381,10 @@ void FGAIMgr::GenerateSimpleAirportTraffic(string ident, double min_dist) {
        double d = dclGetHorizontalSeparation(userpos, aptpos); // in meters
        
        int lev = fgGetInt("/sim/ai-traffic/level");
-       if(lev < 1 || lev > 3) lev = 2;
+       if(lev < 1)
+               return;
+       if (lev > 3)
+               lev = 3;
        if(visibility < 6000) lev = 1;
        //cout << "level = " << lev << '\n';
        
@@ -485,11 +467,12 @@ void FGAIMgr::GenerateSimpleAirportTraffic(string ident, double min_dist) {
                        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);
+                       t->SetModel(cessna ? _defaultModel.get() : (_havePiperModel ? _piperModel.get() : _defaultModel.get()));
                        //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 :-(
@@ -502,7 +485,7 @@ void FGAIMgr::GenerateSimpleAirportTraffic(string ident, double min_dist) {
 
 /*
 // Generate a VFR arrival at airport apt, at least distance d (meters) out.
-void FGAIMgr::GenerateVFRArrival(string apt, double d) {
+void FGAIMgr::GenerateVFRArrival(const string& apt, double d) {
 }
 */
 
@@ -575,7 +558,7 @@ void FGAIMgr::SearchByPos(double range) {
                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));
+                               double sep = dclGetHorizontalSeparation(Point3D(lon, lat, elev), fgGetAirportPos(twd_itr->ident));
                                if(sep < closest) {
                                        closest = sep;
                                        s = twd_itr->ident;
@@ -629,7 +612,7 @@ string FGAIMgr::GenerateUniqueCallsign() {
 }
 
 // This will be moved somewhere else eventually!!!!
-string FGAIMgr::GenerateShortForm(string callsign, string plane_str, bool local) {
+string FGAIMgr::GenerateShortForm(const string& callsign, const string& plane_str, bool local) {
        //cout << callsign << '\n';
        string s;
        if(local) s = "Trainer-";