1
0
Fork 0

Lots of changes to the ATC/AI system for initial revision of random AI GA VFR traffic

This commit is contained in:
daveluff 2004-01-23 17:18:24 +00:00
parent afb654a068
commit a739fad664
25 changed files with 1718 additions and 441 deletions

View file

@ -40,19 +40,39 @@
#include "AIEntity.hxx"
FGAIEntity::FGAIEntity() {
}
FGAIEntity::~FGAIEntity() {
//cout << "FGAIEntity dtor called..." << endl;
_model->deRef(); // Ought to check valid?
//cout << "Removing model from scene graph..." << endl;
globals->get_scenery()->get_scene_graph()->removeKid(_aip.getSceneGraph());
//cout << "Done!" << endl;
}
void FGAIEntity::SetModel(ssgBranch* model) {
_model = model;
_model->ref();
_aip.init(_model);
_aip.setVisible(false);
globals->get_scenery()->get_scene_graph()->addKid(_aip.getSceneGraph());
}
void FGAIEntity::Update(double dt) {
}
string FGAIEntity::GetCallsign() {
return("");
}
void FGAIEntity::RegisterTransmission(int code) {
}
// Run the internal calculations
//void FGAIEntity::Update() {
void FGAIEntity::Transform() {
aip.setPosition(pos.lon(), pos.lat(), pos.elev() * SG_METER_TO_FEET);
aip.setOrientation(roll, pitch, hdg);
aip.update( globals->get_scenery()->get_center() );
_aip.setPosition(_pos.lon(), _pos.lat(), _pos.elev() * SG_METER_TO_FEET);
_aip.setOrientation(_roll, _pitch, _hdg);
_aip.update( globals->get_scenery()->get_center() );
}

View file

@ -41,7 +41,11 @@ class FGAIEntity {
public:
FGAIEntity();
virtual ~FGAIEntity();
// Set the 3D model to use (Must be called)
void SetModel(ssgBranch* model);
// Run the internal calculations
virtual void Update(double dt);
@ -50,17 +54,20 @@ public:
// FIXME int code is a hack - eventually this will receive Alexander's coded messages.
virtual void RegisterTransmission(int code);
inline Point3D GetPos() { return(pos); }
inline Point3D GetPos() { return(_pos); }
virtual string GetCallsign();
protected:
Point3D pos; // WGS84 lat & lon in degrees, elev above sea-level in meters
double hdg; //True heading in degrees
double roll; //degrees
double pitch; //degrees
Point3D _pos; // WGS84 lat & lon in degrees, elev above sea-level in meters
double _hdg; //True heading in degrees
double _roll; //degrees
double _pitch; //degrees
char* model_path; //Path to the 3D model
SGModelPlacement aip;
char* _model_path; //Path to the 3D model
ssgBranch* _model; // Pointer to the model
SGModelPlacement _aip;
void Transform();
};

File diff suppressed because it is too large Load diff

View file

@ -45,7 +45,8 @@ enum TaxiState {
enum OperatingState {
IN_PATTERN,
TAXIING,
PARKED
PARKED,
EN_ROUTE
};
struct StartOfDescent {
@ -58,11 +59,12 @@ class FGAILocalTraffic : public FGAIPlane {
public:
// At the moment we expect the expanded short form callsign - eventually we will just want the reg + type.
FGAILocalTraffic();
~FGAILocalTraffic();
// Initialise
bool Init(string ICAO, OperatingState initialState = PARKED, PatternLeg initialLeg = DOWNWIND);
bool Init(const string& callsign, string ICAO, OperatingState initialState = PARKED, PatternLeg initialLeg = DOWNWIND);
// Run the internal calculations
void Update(double dt);
@ -94,9 +96,38 @@ protected:
// Attempt to enter the traffic pattern in a reasonably intelligent manner
void EnterTrafficPattern(double dt);
// Set up the internal state to be consistent for a downwind entry.
void DownwindEntry();
// Ditto for straight-in
void StraightInEntry(bool des = false);
// Do what is necessary to land and parkup at home airport
void ReturnToBase(double dt);
// Airport/runway/pattern details
string airportID; // The ICAO code of the airport that we're operating around
double aptElev; // Airport elevation
FGGround* ground; // A pointer to the ground control.
FGTower* tower; // A pointer to the tower control.
bool _controlled; // Set true if we find tower control working for the airport, false otherwise.
RunwayDetails rwy;
double patternDirection; // 1 for right, -1 for left (This is double because we multiply/divide turn rates
// with it to get RH/LH turns - DON'T convert it to int under ANY circumstances!!
double glideAngle; // Assumed to be visual glidepath angle for FGAILocalTraffic - can be found at www.airnav.com
// Its conceivable that patternDirection and glidePath could be moved into the RunwayDetails structure.
// Its possible that this might be moved out to the ground/airport class at some point.
FGATCAlignedProjection ortho; // Orthogonal mapping of the local area with the threshold at the origin
// and the runway aligned with the y axis.
void GetAirportDetails(string id);
void GetRwyDetails(string id);
double responseCounter; // timer in seconds to allow response to requests to be a little while after them
// Will almost certainly get moved to FGAIPlane.
private:
FGATCMgr* ATC;
// This is purely for synactic convienience to avoid writing globals->get_ATC_mgr()-> all through the code!
@ -105,21 +136,7 @@ private:
OperatingState operatingState;
int circuitsToFly; //Number of circuits still to do in this session NOT INCLUDING THE CURRENT ONE
bool touchAndGo; //True if circuits should be flown touch and go, false for full stop
// Its possible that this might be moved out to the ground/airport class at some point.
FGATCAlignedProjection ortho; // Orthogonal mapping of the local area with the threshold at the origin
// and the runway aligned with the y axis.
// Airport/runway/pattern details
string airportID; // The ICAO code of the airport that we're operating around
double aptElev; // Airport elevation
FGGround* ground; // A pointer to the ground control.
FGTower* tower; // A pointer to the tower control.
RunwayDetails rwy;
double patternDirection; // 1 for right, -1 for left (This is double because we multiply/divide turn rates
// with it to get RH/LH turns - DON'T convert it to int under ANY circumstances!!
double glideAngle; // Assumed to be visual glidepath angle for FGAILocalTraffic - can be found at www.airnav.com
// Its conceivable that patternDirection and glidePath could be moved into the RunwayDetails structure.
bool transmitted; // Set true when a position report for the current leg has been transmitted.
// Performance characteristics of the plane in knots and ft/min - some of this might get moved out into FGAIPlane
double Vr;
@ -175,14 +192,19 @@ private:
bool reportReadyForDeparture; // set true when ATC has requested that the plane report when ready for departure
bool clearedToLineUp;
bool clearedToTakeOff;
bool _clearedToLand; // also implies cleared for the option.
bool liningUp; // Set true when the turn onto the runway heading is commenced when taxiing out
bool goAround; // Set true if need to go-around
bool goAroundCalled; // Set true during go-around only after we have called our go-around on the radio
bool contactTower; // we have been told to contact tower
bool contactGround; // we have been told to contact ground
bool changeFreq; // true when we need to change frequency
bool _taxiToGA; // Temporary mega-hack indicating we are to taxi to the GA parking and disconnect from tower control.
atc_type changeFreqType; // the service we need to change to
double responseCounter; // timer in seconds to allow response to requests to be a little while after them
bool freeTaxi; // False if the airport has a facilities file with a logical taxi network defined, true if we need to calculate our own taxiing points.
// Hack for getting close to the runway when atan can go pear-shaped
double _savedSlope;
void FlyTrafficPattern(double dt);
@ -201,8 +223,6 @@ private:
void GetNextTaxiNode();
void DoGroundElev();
void GetRwyDetails();
};
#endif // _FG_AILocalTraffic_HXX

View file

@ -23,6 +23,7 @@
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <simgear/math/sg_random.h>
#include <list>
@ -33,9 +34,18 @@
# 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
@ -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; 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);
}

View file

@ -54,19 +54,32 @@ private:
// at any point in or at the end of the list.
// Hence any new access must explicitly first check for atc_list.end() before dereferencing.
// A list of airport ID's
typedef list < string > aptID_list_type;
typedef aptID_list_type::iterator aptID_list_iterator;
// A list of airport or airplane ID's
typedef list < string > ID_list_type;
typedef ID_list_type::iterator ID_list_iterator;
// Temporary storage of ID of planes scheduled for removeal
ID_list_type removalList;
// A map of airport-IDs that have taxiway network files against bucket number
typedef map < int, aptID_list_type* > ai_apt_map_type;
typedef map < int, ID_list_type* > ai_apt_map_type;
typedef ai_apt_map_type::iterator ai_apt_map_iterator;
ai_apt_map_type airports;
ai_apt_map_type facilities;
// A map of airport ID's that we've activated AI traffic at
typedef map < string, int > ai_activated_map_type;
typedef ai_activated_map_type::iterator ai_activated_map_iterator;
ai_activated_map_type activated;
// AI traffic lists mapped by airport
typedef map < string, ai_list_type > ai_traffic_map_type;
typedef ai_traffic_map_type::iterator ai_traffic_map_iterator;
ai_traffic_map_type traffic;
// A map of callsigns that we have used (eg CFGFS or N0546D - the code will generate Cessna-four-six-delta from this later)
typedef map < string, int > ai_callsigns_map_type;
typedef ai_callsigns_map_type::iterator ai_callsigns_map_iterator;
ai_callsigns_map_type ai_callsigns_used;
// Position of the Users Aircraft
double lon;
@ -89,8 +102,15 @@ public:
void unbind();
void update(double dt);
// Signal that it is OK to remove a plane of callsign s
// (To be called by the plane itself).
void ScheduleRemoval(string s);
private:
ssgBranch* _defaultModel; // Cessna 172!
ssgBranch* _piperModel; // pa28-161
bool initDone; // Hack - guard against update getting called before init
@ -98,10 +118,19 @@ private:
//void RemoveFromList(const char* id, atc_type tp);
// Activate AI traffic at an airport
void ActivateAirport(string id);
void ActivateAirport(string ident);
// Hack - Generate AI traffic at an airport with no facilities file, with the first plane being at least min_dist out.
void GenerateSimpleAirportTraffic(string ident, double min_dist = 0.0);
// Search for valid airports in the vicinity of the user and activate them if necessary
void SearchByPos(double range);
string GenerateCallsign();
string GenerateUniqueCallsign();
string GenerateShortForm(string callsign, string plane_str = "Cessna-", bool local = false);
};

View file

@ -53,6 +53,7 @@ void FGAIPlane::Update(double dt) {
if(_pending) {
if(tuned_station) {
if(tuned_station->GetFreqClear()) {
//cout << "TUNED STATION FREQ CLEAR\n";
tuned_station->SetFreqInUse();
_pending = false;
_transmit = true;
@ -69,6 +70,7 @@ void FGAIPlane::Update(double dt) {
}
} else {
// Not tuned to ATC - Just go ahead and transmit
//cout << "NOT TUNED TO ATC\n";
_pending = false;
_transmit = true;
_transmitting = false;
@ -115,16 +117,16 @@ void FGAIPlane::Update(double dt) {
void FGAIPlane::Bank(double angle) {
// This *should* bank us smoothly to any angle
if(fabs(roll - angle) > 0.6) {
roll -= ((roll - angle)/fabs(roll - angle));
if(fabs(_roll - angle) > 0.6) {
_roll -= ((_roll - angle)/fabs(_roll - angle));
}
}
// Duplication of Bank(0.0) really - should I cut this?
void FGAIPlane::LevelWings(void) {
// bring the plane back to level smoothly (this should work to come out of either bank)
if(fabs(roll) > 0.6) {
roll -= (roll/fabs(roll));
if(fabs(_roll) > 0.6) {
_roll -= (_roll/fabs(_roll));
}
}

View file

@ -83,6 +83,9 @@ public:
// Return what type of landing we're doing on this circuit
virtual LandingType GetLandingOption();
// Return the callsign
inline string GetCallsign() {return plane.callsign;}
protected:
PlaneRec plane;

View file

@ -34,8 +34,10 @@ FGATC::FGATC() {
receiving = false;
respond = false;
runResponseCounter = false;
_runReleaseCounter = false;
responseID = "";
responseReqd = false;
_type = INVALID;
}
FGATC::~FGATC() {
@ -53,6 +55,15 @@ void FGATC::Update(double dt) {
responseCounter += dt;
}
}
if(_runReleaseCounter) {
if(_releaseCounter >= _releaseTime) {
freqClear = true;
_runReleaseCounter = false;
} else {
_releaseCounter += dt;
}
}
}
void FGATC::ReceiveUserCallback(int code) {
@ -71,12 +82,15 @@ void FGATC::SetResponseReqd(string rid) {
}
void FGATC::NotifyTransmissionFinished(string rid) {
//cout << "Transmission finished, callsign = " << rid << '\n';
receiving = false;
responseID = rid;
if(responseReqd) {
runResponseCounter = true;
responseCounter = 0.0;
responseTime = 1.8; // TODO - randomize this slightly.
responseTime = 1.2; // TODO - randomize this slightly, and allow it to be dependent on the transmission and how busy the ATC is.
respond = false; // TODO - this ignores the fact that more than one plane could call this before response
// Shouldn't happen with AI only, but user could confuse things??
} else {
freqClear = true;
}
@ -95,10 +109,6 @@ void FGATC::SetDisplay() {
void FGATC::SetNoDisplay() {
}
atc_type FGATC::GetType() {
return INVALID;
}
void FGATC::SetData(ATCData* d) {
lon = d->lon;
lat = d->lat;

View file

@ -148,7 +148,7 @@ public:
// The user will just have to wait for a gap in dialog as in real life.
// Return the type of ATC station that the class represents
virtual atc_type GetType();
inline atc_type GetType() { return _type; }
// Set the core ATC data
void SetData(ATCData* d);
@ -192,6 +192,7 @@ protected:
int range;
string ident; // Code of the airport its at.
string name; // Name transmitted in the broadcast.
atc_type _type;
// Rendering related stuff
bool voice; // Flag - true if we are using voice
@ -209,6 +210,9 @@ protected:
string responseID; // ID of the plane to respond to
bool respond; // Flag to indicate now is the time to respond - ie set following the count down of the response timer.
// Derived classes only need monitor this flag, and use the response ID, as long as they call FGATC::Update(...)
bool _runReleaseCounter; // A timer for releasing the frequency after giving the message enough time to display
double _releaseTime;
double _releaseCounter;
};
inline istream&

View file

@ -260,7 +260,7 @@ void FGATCDialog::add_entry(string station, string transmission, string menutext
void FGATCDialog::remove_entry( const string &station, const string &trans, atc_type type ) {
atcmentry_vec_type* p = &((available_dialog[type])[station]);
atcmentry_vec_iterator current = p->begin();
atcmentry_vec_iterator current = p->begin();
while(current != p->end()) {
if(current->transmission == trans) current = p->erase(current);
else ++current;
@ -269,7 +269,7 @@ void FGATCDialog::remove_entry( const string &station, const string &trans, atc_
void FGATCDialog::remove_entry( const string &station, int code, atc_type type ) {
atcmentry_vec_type* p = &((available_dialog[type])[station]);
atcmentry_vec_iterator current = p->begin();
atcmentry_vec_iterator current = p->begin();
while(current != p->end()) {
if(current->callback_code == code) current = p->erase(current);
else ++current;
@ -279,7 +279,7 @@ void FGATCDialog::remove_entry( const string &station, int code, atc_type type )
// query the database whether the transmission is already registered;
bool FGATCDialog::trans_reg( const string &station, const string &trans, atc_type type ) {
atcmentry_vec_type* p = &((available_dialog[type])[station]);
atcmentry_vec_iterator current = p->begin();
atcmentry_vec_iterator current = p->begin();
for ( ; current != p->end() ; ++current ) {
if ( current->transmission == trans ) return true;
}
@ -289,7 +289,7 @@ bool FGATCDialog::trans_reg( const string &station, const string &trans, atc_typ
// query the database whether the transmission is already registered;
bool FGATCDialog::trans_reg( const string &station, int code, atc_type type ) {
atcmentry_vec_type* p = &((available_dialog[type])[station]);
atcmentry_vec_iterator current = p->begin();
atcmentry_vec_iterator current = p->begin();
for ( ; current != p->end() ; ++current ) {
if ( current->callback_code == code ) return true;
}
@ -397,14 +397,23 @@ void FGATCDialog::PopupCallback() {
break;
}
} else if(atcptr->GetType() == TOWER) {
ATCMenuEntry a = ((available_dialog[TOWER])[(string)atcptr->get_ident()])[atcDialogCommunicationOptions->getValue()];
atcptr->SetFreqInUse();
globals->get_ATC_display()->RegisterSingleMessage(atcptr->GenText(a.transmission, a.callback_code));
_callbackPending = true;
_callbackTimer = 0.0;
_callbackWait = 5.0;
_callbackPtr = atcptr;
_callbackCode = a.callback_code;
//cout << "TOWER " << endl;
//cout << "ident is " << atcptr->get_ident() << endl;
atcmentry_vec_type atcmlist = (available_dialog[TOWER])[(string)atcptr->get_ident()];
if(atcmlist.size()) {
//cout << "Doing callback...\n";
ATCMenuEntry a = atcmlist[atcDialogCommunicationOptions->getValue()];
atcptr->SetFreqInUse();
globals->get_ATC_display()->RegisterSingleMessage(atcptr->GenText(a.transmission, a.callback_code));
_callbackPending = true;
_callbackTimer = 0.0;
_callbackWait = 5.0;
_callbackPtr = atcptr;
_callbackCode = a.callback_code;
} else {
//cout << "No options available...\n";
}
//cout << "Donded" << endl;
}
}
}

View file

@ -35,8 +35,8 @@
FGATCDisplay::FGATCDisplay() {
rep_msg = false;
change_msg_flag = false;
dsp_offset1 = 0;
dsp_offset2 = 0;
dsp_offset1 = 0.0;
dsp_offset2 = 0.0;
}
@ -195,6 +195,7 @@ void FGATCDisplay::update(double dt) {
}
void FGATCDisplay::RegisterSingleMessage(string msg, double delay) {
//cout << msg << '\n';
atcMessage m;
m.msg = msg;
m.repeating = false;

View file

@ -48,6 +48,7 @@ AirportATC::AirportATC() :
ground_active(false),
set_by_AI(false),
numAI(0)
//airport_atc_map.clear();
{
for(int i=0; i<ATC_NUM_TYPES; ++i) {
set_by_comm[0][i] = false;
@ -58,9 +59,9 @@ AirportATC::AirportATC() :
FGATCMgr::FGATCMgr() {
comm_ident[0] = "";
comm_ident[1] = "";
last_comm_ident[0] = "";
last_comm_ident[1] = "";
approach_ident = "";
//last_comm_ident[0] = "";
//last_comm_ident[1] = "";
//approach_ident = "";
last_in_range = false;
comm_type[0] = INVALID;
comm_type[1] = INVALID;
@ -83,6 +84,8 @@ void FGATCMgr::unbind() {
}
void FGATCMgr::init() {
//cout << "ATCMgr::init called..." << endl;
comm_node[0] = fgGetNode("/radios/comm[0]/frequencies/selected-mhz", true);
comm_node[1] = fgGetNode("/radios/comm[1]/frequencies/selected-mhz", true);
lon_node = fgGetNode("/position/longitude-deg", true);
@ -135,6 +138,7 @@ void FGATCMgr::init() {
current_atcdialog->Init();
initDone = true;
//cout << "ATCmgr::init done!" << endl;
}
void FGATCMgr::update(double dt) {
@ -149,7 +153,7 @@ void FGATCMgr::update(double dt) {
//Traverse the list of active stations.
//Only update one class per update step to avoid the whole ATC system having to calculate between frames.
//Eventually we should only update every so many steps.
//cout << "In FGATCMgr::update - atc_list.size = " << atc_list.size() << '\n';
//cout << "In FGATCMgr::update - atc_list.size = " << atc_list.size() << endl;
if(atc_list.size()) {
if(atc_list_itr == atc_list.end()) {
atc_list_itr = atc_list.begin();
@ -161,6 +165,14 @@ void FGATCMgr::update(double dt) {
++atc_list_itr;
}
/*
cout << "ATC_LIST: " << atc_list.size() << ' ';
for(atc_list_iterator it = atc_list.begin(); it != atc_list.end(); it++) {
cout << (*it)->get_ident() << ' ';
}
cout << '\n';
*/
// Search the tuned frequencies every now and then - this should be done with the event scheduler
static int i = 0; // Very ugly - but there should only ever be one instance of FGATCMgr.
/*
@ -197,6 +209,7 @@ unsigned short int FGATCMgr::GetFrequency(string ident, atc_type tp) {
// Might need more sophistication in this in the future - eg registration by aircraft call-sign.
bool FGATCMgr::AIRegisterAirport(string ident) {
SG_LOG(SG_ATC, SG_BULK, "AI registered airport " << ident << " with the ATC system");
//cout << "AI registered airport " << ident << " with the ATC system" << '\n';
if(airport_atc_map.find(ident) != airport_atc_map.end()) {
airport_atc_map[ident]->set_by_AI = true;
airport_atc_map[ident]->numAI++;
@ -234,9 +247,19 @@ bool FGATCMgr::AIRegisterAirport(string ident) {
// Channel is zero based
bool FGATCMgr::CommRegisterAirport(string ident, int chan, atc_type tp) {
SG_LOG(SG_ATC, SG_BULK, "Comm channel " << chan << " registered airport " << ident);
//cout << "Comm channel " << chan << " registered airport " << ident << '\n';
if(airport_atc_map.find(ident) != airport_atc_map.end()) {
//cout << "IN MAP - flagging set by comm..." << endl;
airport_atc_map[ident]->set_by_comm[chan][tp] = true;
if(tp == ATIS) {
airport_atc_map[ident]->atis_active = true;
} else if(tp == TOWER) {
airport_atc_map[ident]->tower_active = true;
} else if(tp == GROUND) {
airport_atc_map[ident]->ground_active = true;
} else if(tp == APPROACH) {
//a->approach_active = true;
} // TODO - there *must* be a better way to do this!!!
return(true);
} else {
//cout << "NOT IN MAP - creating new..." << endl;
@ -253,6 +276,15 @@ bool FGATCMgr::CommRegisterAirport(string ident, int chan, atc_type tp) {
a->tower_active = false;
a->ground_freq = GetFrequency(ident, GROUND);
a->ground_active = false;
if(tp == ATIS) {
a->atis_active = true;
} else if(tp == TOWER) {
a->tower_active = true;
} else if(tp == GROUND) {
a->ground_active = true;
} else if(tp == APPROACH) {
//a->approach_active = true;
} // TODO - there *must* be a better way to do this!!!
// TODO - some airports will have a tower/ground frequency but be inactive overnight.
a->set_by_AI = false;
a->numAI = 0;
@ -267,8 +299,9 @@ bool FGATCMgr::CommRegisterAirport(string ident, int chan, atc_type tp) {
// Remove from list only if not needed by the AI system or the other comm channel
// Note that chan is zero based.
void FGATCMgr::CommRemoveFromList(const char* id, atc_type tp, int chan) {
void FGATCMgr::CommRemoveFromList(string id, atc_type tp, int chan) {
SG_LOG(SG_ATC, SG_BULK, "CommRemoveFromList called for airport " << id << " " << tp << " by channel " << chan);
//cout << "CommRemoveFromList called for airport " << id << " " << tp << " by channel " << chan << '\n';
if(airport_atc_map.find(id) != airport_atc_map.end()) {
AirportATC* a = airport_atc_map[id];
//cout << "In CommRemoveFromList, a->ground_freq = " << a->ground_freq << endl;
@ -309,12 +342,16 @@ void FGATCMgr::CommRemoveFromList(const char* id, atc_type tp, int chan) {
a->set_by_comm[0][tp] = false;
// Remove only if not also set by the other comm channel
if(!a->set_by_comm[1][tp]) {
a->tower_active = false;
a->ground_active = false;
RemoveFromList(id, tp);
}
break;
case 1:
a->set_by_comm[1][tp] = false;
if(!a->set_by_comm[0][tp]) {
a->tower_active = false;
a->ground_active = false;
RemoveFromList(id, tp);
}
break;
@ -326,24 +363,26 @@ void FGATCMgr::CommRemoveFromList(const char* id, atc_type tp, int chan) {
// Remove from list - should only be called from above or similar
// This function *will* remove it from the list regardless of who else might want it.
void FGATCMgr::RemoveFromList(const char* id, atc_type tp) {
//cout << "Requested type = " << tp << '\n';
//cout << "id = " << id << '\n';
atc_list_itr = atc_list.begin();
while(atc_list_itr != atc_list.end()) {
//cout << "type = " << (*atc_list_itr)->GetType() << '\n';
//cout << "Ident = " << (*atc_list_itr)->get_ident() << '\n';
if( (!strcmp((*atc_list_itr)->get_ident(), id))
&& ((*atc_list_itr)->GetType() == tp) ) {
//Before removing it stop it transmitting!!
//cout << "OBLITERATING FROM LIST!!!\n";
(*atc_list_itr)->SetNoDisplay();
(*atc_list_itr)->Update(0.00833);
delete (*atc_list_itr);
atc_list_itr = atc_list.erase(atc_list_itr);
break;
} // Note that that can upset where we are in the list but that doesn't really matter
++atc_list_itr;
void FGATCMgr::RemoveFromList(string id, atc_type tp) {
//cout << "FGATCMgr::RemoveFromList called..." << endl;
//cout << "Requested type = " << tp << endl;
//cout << "id = " << id << endl;
atc_list_iterator it = atc_list.begin();
while(it != atc_list.end()) {
//cout << "type = " << (*it)->GetType() << '\n';
//cout << "Ident = " << (*it)->get_ident() << '\n';
if( (!strcmp((*it)->get_ident(), id.c_str()))
&& ((*it)->GetType() == tp) ) {
//Before removing it stop it transmitting!!
//cout << "OBLITERATING FROM LIST!!!\n";
(*it)->SetNoDisplay();
(*it)->Update(0.00833);
delete (*it);
atc_list.erase(it);
atc_list_itr = atc_list.begin(); // Reset the persistent itr incase we've left it off the end.
break;
}
++it;
}
}
@ -351,16 +390,18 @@ void FGATCMgr::RemoveFromList(const char* id, atc_type tp) {
// Find in list - return a currently active ATC pointer given ICAO code and type
// Return NULL if the given service is not in the list
// - *** THE CALLING FUNCTION MUST CHECK FOR THIS ***
FGATC* FGATCMgr::FindInList(const char* id, atc_type tp) {
atc_list_itr = atc_list.begin();
while(atc_list_itr != atc_list.end()) {
if( (!strcmp((*atc_list_itr)->get_ident(), id))
&& ((*atc_list_itr)->GetType() == tp) ) {
return(*atc_list_itr);
} // Note that that can upset where we are in the list but that shouldn't really matter
++atc_list_itr;
FGATC* FGATCMgr::FindInList(string id, atc_type tp) {
//cout << "Entering FindInList for " << id << ' ' << tp << endl;
atc_list_iterator it = atc_list.begin();
while(it != atc_list.end()) {
if( (!strcmp((*it)->get_ident(), id.c_str()))
&& ((*it)->GetType() == tp) ) {
return(*it);
}
++it;
}
// If we get here it's not in the list
//cout << "Couldn't find it in the list though :-(" << endl;
return(NULL);
}
@ -381,8 +422,10 @@ bool FGATCMgr::GetAirportATCDetails(string icao, AirportATC* a) {
// - at the moment all these GetATC... functions exposed are just too complicated.
FGATC* FGATCMgr::GetATCPointer(string icao, atc_type type) {
if(airport_atc_map.find(icao) == airport_atc_map.end()) {
//cout << "Unable to find " << icao << ' ' << type << " in the airport_atc_map" << endl;
return NULL;
}
//cout << "Found " << icao << ' ' << type << endl;
AirportATC *a = airport_atc_map[icao];
//cout << "a->lon = " << a->lon << '\n';
//cout << "a->elev = " << a->elev << '\n';
@ -400,6 +443,7 @@ FGATC* FGATCMgr::GetATCPointer(string icao, atc_type type) {
atc_list.push_back(t);
a->tower_active = true;
airport_atc_map[icao] = a;
//cout << "Initing tower in GetATCPointer()\n";
t->Init();
return(t);
} else {
@ -441,6 +485,7 @@ FGATC* FGATCMgr::GetATCPointer(string icao, atc_type type) {
}
SG_LOG(SG_ATC, SG_ALERT, "ERROR IN FGATCMgr - reached end of GetATCPointer");
//cout << "ERROR IN FGATCMgr - reached end of GetATCPointer" << endl;
return(NULL);
}
@ -503,7 +548,7 @@ void FGATCMgr::FreqSearch(int channel) {
}
}
// At this point we can assume that we need to add the service.
comm_ident[chan] = (data.ident).c_str();
comm_ident[chan] = data.ident;
comm_type[chan] = data.type;
comm_x[chan] = (double)data.x;
comm_y[chan] = (double)data.y;
@ -520,6 +565,7 @@ void FGATCMgr::FreqSearch(int channel) {
if(app != NULL) {
// The station is already in the ATC list
//cout << "In list - flagging SetDisplay..." << endl;
comm_atc_ptr[chan] = app;
app->SetDisplay();
} else {
// Generate the station and put in the ATC list
@ -532,19 +578,24 @@ void FGATCMgr::FreqSearch(int channel) {
atc_list.push_back(a);
}
} else if (comm_type[chan] == TOWER) {
//cout << "TOWER TOWER TOWER\n";
CommRegisterAirport(comm_ident[chan], chan, TOWER);
//cout << "Done (TOWER)" << endl;
FGATC* app = FindInList(comm_ident[chan], TOWER);
if(app != NULL) {
// The station is already in the ATC list
SG_LOG(SG_GENERAL, SG_DEBUG, comm_ident[chan] << " is in list - flagging SetDisplay...");
SG_LOG(SG_GENERAL, SG_DEBUG, comm_ident[chan] << " is in list - flagging SetDisplay...");
//cout << comm_ident[chan] << " is in list - flagging SetDisplay...\n";
comm_atc_ptr[chan] = app;
app->SetDisplay();
} else {
// Generate the station and put in the ATC list
SG_LOG(SG_GENERAL, SG_DEBUG, comm_ident[chan] << " is not in list - generating...");
SG_LOG(SG_GENERAL, SG_DEBUG, comm_ident[chan] << " is not in list - generating...");
//cout << comm_ident[chan] << " is not in list - generating...\n";
FGTower* t = new FGTower;
t->SetData(&data);
comm_atc_ptr[chan] = t;
//cout << "Initing tower in FreqSearch()\n";
t->Init();
t->SetDisplay();
atc_list.push_back(t);
@ -555,6 +606,7 @@ void FGATCMgr::FreqSearch(int channel) {
FGATC* app = FindInList(comm_ident[chan], GROUND);
if(app != NULL) {
// The station is already in the ATC list
comm_atc_ptr[chan] = app;
app->SetDisplay();
} else {
// Generate the station and put in the ATC list
@ -638,19 +690,21 @@ void FGATCMgr::AreaSearch() {
// remove planes which are out of range
// TODO - I'm not entirely sure that this belongs here.
atc_list_itr = atc_list.begin();
while(atc_list_itr != atc_list.end()) {
if((*atc_list_itr)->GetType() == APPROACH ) {
int np = (*atc_list_itr)->RemovePlane();
atc_list_iterator it = atc_list.begin();
while(it != atc_list.end()) {
if((*it)->GetType() == APPROACH ) {
int np = (*it)->RemovePlane();
// if approach has no planes left remove it from ATC list
if ( np == 0) {
(*atc_list_itr)->SetNoDisplay();
(*atc_list_itr)->Update(0.00833);
delete (*atc_list_itr);
atc_list_itr = atc_list.erase(atc_list_itr);
//cout << "REMOVING AN APPROACH STATION WITH NO PLANES..." << endl;
(*it)->SetNoDisplay();
(*it)->Update(0.00833);
delete (*it);
atc_list.erase(it);
atc_list_itr = atc_list.begin(); // Reset the persistent itr incase we've left it off the end.
break; // the other stations will be checked next time
}
}
++atc_list_itr;
++it;
}
}

View file

@ -128,10 +128,9 @@ private:
double comm_range[2], comm_effective_range[2];
bool comm_valid[2];
const char* comm_ident[2];
const char* last_comm_ident[2];
const char* approach_ident;
string comm_ident[2];
//string last_comm_ident[2];
//string approach_ident;
bool last_in_range;
//FGATIS atis;
@ -195,17 +194,17 @@ private:
// Remove a class from the atc_list and delete it from memory
// *if* no other comm channel or AI plane is using it.
void CommRemoveFromList(const char* id, atc_type tp, int chan);
void CommRemoveFromList(string id, atc_type tp, int chan);
// Remove a class from the atc_list and delete it from memory
// Should be called from the above - not directly!!
void RemoveFromList(const char* id, atc_type tp);
void RemoveFromList(string id, atc_type tp);
// Return a pointer to a class in the list given ICAO code and type
// (external interface to this is through GetATCPointer)
// Return NULL if the given service is not in the list
// - *** THE CALLING FUNCTION MUST CHECK FOR THIS ***
FGATC* FindInList(const char* id, atc_type tp);
FGATC* FindInList(string id, atc_type tp);
// Search the specified channel for stations on the same frequency and in range.
void FreqSearch(int channel);

View file

@ -114,38 +114,71 @@ string ConvertRwyNumToSpokenString(string s) {
// Return the phonetic letter of a letter represented as an integer 1->26
string GetPhoneticIdent(int i) {
// TODO - Check i is between 1 and 26 and wrap if necessary
switch(i) {
case 1 : return("alpha");
case 2 : return("bravo");
case 3 : return("charlie");
case 4 : return("delta");
case 5 : return("echo");
case 6 : return("foxtrot");
case 7 : return("golf");
case 8 : return("hotel");
case 9 : return("india");
case 10 : return("juliet");
case 11 : return("kilo");
case 12 : return("lima");
case 13 : return("mike");
case 14 : return("november");
case 15 : return("oscar");
case 16 : return("papa");
case 17 : return("quebec");
case 18 : return("romeo");
case 19 : return("sierra");
case 20 : return("tango");
case 21 : return("uniform");
case 22 : return("victor");
case 23 : return("whiskey");
case 24 : return("x-ray");
case 25 : return("yankee");
case 26 : return("zulu");
return(GetPhoneticIdent(char('a' + (i-1))));
}
// Return the phonetic letter of a character in the range a-z or A-Z.
// Currently always returns prefixed by lowercase.
string GetPhoneticIdent(char c) {
c = tolower(c);
// TODO - Check c is between a and z and wrap if necessary
switch(c) {
case 'a' : return("alpha");
case 'b' : return("bravo");
case 'c' : return("charlie");
case 'd' : return("delta");
case 'e' : return("echo");
case 'f' : return("foxtrot");
case 'g' : return("golf");
case 'h' : return("hotel");
case 'i' : return("india");
case 'j' : return("juliet");
case 'k' : return("kilo");
case 'l' : return("lima");
case 'm' : return("mike");
case 'n' : return("november");
case 'o' : return("oscar");
case 'p' : return("papa");
case 'q' : return("quebec");
case 'r' : return("romeo");
case 's' : return("sierra");
case 't' : return("tango");
case 'u' : return("uniform");
case 'v' : return("victor");
case 'w' : return("whiskey");
case 'x' : return("x-ray");
case 'y' : return("yankee");
case 'z' : return("zulu");
}
// We shouldn't get here
return("Error");
}
// Get the compass direction associated with a heading in degrees
// Currently returns 8 direction resolution (N, NE, E etc...)
// Might be modified in future to return 4, 8 or 16 resolution but defaulting to 8.
string GetCompassDirection(double h) {
while(h < 0.0) h += 360.0;
while(h > 360.0) h -= 360.0;
if(h < 22.5 || h > 337.5) {
return("North");
} else if(h < 67.5) {
return("North-East");
} else if(h < 112.5) {
return("East");
} else if(h < 157.5) {
return("South-East");
} else if(h < 202.5) {
return("South");
} else if(h < 247.5) {
return("South-West");
} else if(h < 292.5) {
return("West");
} else {
return("North-West");
}
}
//================================================================================================================
// Given two positions (lat & lon in degrees), get the HORIZONTAL separation (in meters)
@ -305,16 +338,31 @@ double dclGetAirportElev( const string& id ) {
FGAirport a;
// double lon, lat;
SG_LOG( SG_GENERAL, SG_INFO,
SG_LOG( SG_ATC, SG_INFO,
"Finding elevation for airport: " << id );
if ( dclFindAirportID( id, &a ) ) {
return a.elevation;
return a.elevation * SG_FEET_TO_METER;
} else {
return -9999.0;
}
}
// get airport position
Point3D dclGetAirportPos( const string& id ) {
FGAirport a;
// double lon, lat;
SG_LOG( SG_ATC, SG_INFO,
"Finding position for airport: " << id );
if ( dclFindAirportID( id, &a ) ) {
return Point3D(a.longitude, a.latitude, a.elevation);
} else {
return Point3D(0.0, 0.0, -9999.0);
}
}
// Runway stuff
// Given a Point3D (lon/lat/elev) and an FGRunway struct, determine if the point lies on the runway
bool OnRunway(Point3D pt, const FGRunway& rwy) {

View file

@ -56,6 +56,14 @@ string ConvertRwyNumToSpokenString(string s);
// Return the phonetic letter of a letter represented as an integer 1->26
string GetPhoneticIdent(int i);
// Return the phonetic letter of a character in the range a-z or A-Z.
// Currently always returns prefixed by lowercase.
string GetPhoneticIdent(char c);
// Get the compass direction associated with a heading in degrees
// Currently returns 8 direction resolution (N, NE, E etc...)
// Might be modified in future to return 4, 8 or 16 resolution but defaulting to 8.
string GetCompassDirection(double h);
/*******************************
*
@ -97,9 +105,12 @@ double GetAngleDiff_deg( const double &a1, const double &a2);
// find basic airport location info from airport database
bool dclFindAirportID( const string& id, FGAirport *a );
// get airport elevation
// get airport elevation IN METERS
double dclGetAirportElev( const string& id );
// get airport position (elev portion in FEET)
Point3D dclGetAirportPos( const string& id );
/****************
*
* Runways

View file

@ -17,6 +17,7 @@ libATC_a_SOURCES = \
AIEntity.hxx AIEntity.cxx \
AIPlane.hxx AIPlane.cxx \
AILocalTraffic.hxx AILocalTraffic.cxx \
AIGAVFRTraffic.hxx AIGAVFRTraffic.cxx \
transmission.hxx transmission.cxx transmissionlist.hxx transmissionlist.cxx
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src

View file

@ -42,6 +42,8 @@
FGApproach::FGApproach(){
comm1_node = fgGetNode("/radios/comm[0]/frequencies/selected-mhz", true);
comm2_node = fgGetNode("/radios/comm[1]/frequencies/selected-mhz", true);
_type = APPROACH;
num_planes = 0;
lon_node = fgGetNode("/position/longitude-deg", true);

View file

@ -169,7 +169,6 @@ public:
inline double get_bucket() const { return bucket; }
inline int get_pnum() const { return num_planes; }
inline string get_trans_ident() { return trans_ident; }
inline atc_type GetType() { return APPROACH; }
private:

View file

@ -64,6 +64,7 @@ FGATIS::FGATIS() :
{
vPtr = globals->get_ATC_mgr()->GetVoicePointer(ATIS);
voiceOK = (vPtr == NULL ? false : true);
_type = ATIS;
}
FGATIS::~FGATIS() {

View file

@ -86,7 +86,6 @@ class FGATIS : public FGATC {
//Indicate that this instance should not be outputting to the ATC display
inline void SetNoDisplay(void) {display = false;}
inline atc_type GetType() { return ATIS; }
//inline void set_type(const atc_type tp) {type = tp;}
inline string get_trans_ident() { return trans_ident; }
inline void set_refname(string r) { refname = r; }

View file

@ -53,6 +53,7 @@ a_path::a_path() {
FGGround::FGGround() {
ATCmgr = globals->get_ATC_mgr();
_type = GROUND;
display = false;
networkLoadOK = false;
ground_traffic.erase(ground_traffic.begin(), ground_traffic.end());
@ -454,6 +455,11 @@ Gate* FGGround::GetGateNode() {
}
node* FGGround::GetHoldShortNode(string rwyID) {
return(NULL); // TODO - either implement me or remove me!!!
}
// WARNING - This is hardwired to my prototype logical network format
// and will almost certainly change when Bernie's stuff comes on-line.
// Returns NULL if it can't find a valid node.
@ -495,7 +501,7 @@ ground_network_path_type FGGround::GetPath(node* A, node* B) {
ground_network_path_type FGGround::GetPath(node* A, string rwyID) {
node* b = GetThresholdNode(rwyID);
if(b == NULL) {
SG_LOG(SG_ATC, SG_ALERT, "ERROR - unable to find path to runway theshold in ground.cxx\n");
SG_LOG(SG_ATC, SG_ALERT, "ERROR - unable to find path to runway theshold in ground.cxx for airport " << ident << '\n');
ground_network_path_type emptyPath;
emptyPath.erase(emptyPath.begin(), emptyPath.end());
return(emptyPath);

View file

@ -232,7 +232,6 @@ public:
void Update(double dt);
inline string get_trans_ident() { return trans_ident; }
inline atc_type GetType() { return GROUND; }
inline void SetDisplay() {display = true;}
inline void SetNoDisplay() {display = false;}
@ -258,6 +257,9 @@ public:
// Return a pointer to an unused gate
Gate* GetGateNode();
// Return a pointer to a hold short node
node* GetHoldShortNode(string rwyID);
// Runway stuff - this might change in the future.
// Get a list of exits from a given runway
// It is up to the calling function to check for non-zero size of returned array before use

File diff suppressed because it is too large Load diff

View file

@ -60,7 +60,8 @@ enum tower_callback_type {
USER_REQUEST_VFR_ARRIVAL_TOUCH_AND_GO = 4,
USER_REPORT_3_MILE_FINAL = 5,
USER_REPORT_DOWNWIND = 6,
USER_REPORT_RWY_VACATED = 7
USER_REPORT_RWY_VACATED = 7,
USER_REPORT_GOING_AROUND = 8
};
// TODO - need some differentiation of IFR and VFR traffic in order to give the former priority.
@ -135,6 +136,8 @@ public:
// eg "Cessna Charlie Foxtrot Golf Foxtrot Sierra eight miles South of the airport for full stop with Bravo"
// This function probably only called via user interaction - AI planes will have an overloaded function taking a planerec.
void VFRArrivalContact(string ID, LandingType opt = AIP_LT_UNKNOWN);
// For the AI planes...
void VFRArrivalContact(PlaneRec plane, FGAIPlane* requestee, LandingType lt = AIP_LT_UNKNOWN);
void RequestDepartureClearance(string ID);
void ReportFinal(string ID);
@ -154,6 +157,9 @@ public:
// CAUTION - currently it is assumed that this plane's callsign is unique - it is up to AIMgr to generate unique callsigns.
void RegisterAIPlane(PlaneRec plane, FGAIPlane* ai, tower_traffic_type op, PatternLeg lg = LEG_UNKNOWN);
// Deregister and remove an AI plane.
void DeregisterAIPlane(string id);
// Public interface to the active runway - this will get more complex
// in the future and consider multi-runway use, airplane weight etc.
inline string GetActiveRunway() { return activeRwy; }
@ -165,7 +171,6 @@ public:
inline void SetNoDisplay() { display = false; }
inline string get_trans_ident() { return trans_ident; }
inline atc_type GetType() { return TOWER; }
inline FGGround* GetGroundPtr() { return ground; }
@ -184,13 +189,17 @@ private:
// Respond to a transmission
void Respond();
void CheckHoldList(double dt);
void CheckCircuitList(double dt);
void ProcessRunwayVacatedReport(TowerPlaneRec* t);
// Remove all options from the user dialog choice
void RemoveAllUserDialogOptions();
// Periodic checks on the various traffic.
void CheckHoldList(double dt);
void CheckCircuitList(double dt);
void CheckRunwayList(double dt);
void CheckApproachList(double dt);
void CheckDepartureList(double dt);
// Currently this assumes we *are* next on the runway and doesn't check for planes about to land -
// this should be done prior to calling this function.
@ -199,6 +208,9 @@ private:
// Find a pointer to plane of callsign ID within the internal data structures
TowerPlaneRec* FindPlane(string ID);
// Remove and delete all instances of a plane with a given ID
void RemovePlane(string ID);
// Figure out if a given position lies on the active runway
// Might have to change when we consider more than one active rwy.
bool OnActiveRunway(Point3D pt);
@ -292,8 +304,14 @@ private:
tower_plane_rec_list_type trafficList; // TODO - needs to be expandable to more than one rwy
tower_plane_rec_list_iterator trafficListItr;
// List of planes that have vacated the runway inbound but not yet handed off to ground
tower_plane_rec_list_type vacatedList;
tower_plane_rec_list_iterator vacatedListItr;
// Returns true if successful
bool RemoveFromTrafficList(string id);
bool RemoveFromAppList(string id);
bool RemoveFromRwyList(string id);
// Return the ETA of plane no. list_pos (1-based) in the traffic list.
// i.e. list_pos = 1 implies next to use runway.
@ -311,6 +329,9 @@ private:
bool separateGround; // true if ground control is separate
FGGround* ground; // The ground control associated with this airport.
bool _departureControlled; // true if we need to hand off departing traffic to departure control
//FGDeparture* _departure; // The relevant departure control (once we've actually written it!)
// for failure modeling
string trans_ident; // transmitted ident
bool tower_failed; // tower failed?