X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FATC%2FAIMgr.cxx;h=7c9e60350241357f74c33c6ab6af56742372814e;hb=a739fad66484506ebb71fc802bf8f1a0c2ab4c6c;hp=d44a845114339721d29772b4f2e48a984933ccfa;hpb=62854c99a62f477cc7df48295991f6cfe3be978a;p=flightgear.git diff --git a/src/ATC/AIMgr.cxx b/src/ATC/AIMgr.cxx index d44a84511..7c9e60350 100644 --- a/src/ATC/AIMgr.cxx +++ b/src/ATC/AIMgr.cxx @@ -23,6 +23,7 @@ #include
#include
+#include #include @@ -33,9 +34,18 @@ # include // for directory reading #endif +#ifdef FG_WEATHERCM +# include +#else +# include +# include +#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 @@ -88,12 +118,12 @@ void FGAIMgr::init() { 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; } SG_LOG(SG_ATC, SG_BULK, "Mapping " << f_ident << " to bucket " << idx); } @@ -119,12 +149,12 @@ void FGAIMgr::init() { 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; } SG_LOG(SG_ATC, SG_BULK, "Mapping " << f_ident << " to bucket " << idx); } @@ -135,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() { @@ -152,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; @@ -163,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) { @@ -189,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 @@ -212,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"; @@ -220,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; @@ -239,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; @@ -248,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; i0; --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); }