Lots of changes to the ATC/AI system for initial revision of random AI GA VFR traffic
This commit is contained in:
parent
afb654a068
commit
a739fad664
25 changed files with 1718 additions and 441 deletions
|
@ -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() );
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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&
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ FGATIS::FGATIS() :
|
|||
{
|
||||
vPtr = globals->get_ATC_mgr()->GetVoicePointer(ATIS);
|
||||
voiceOK = (vPtr == NULL ? false : true);
|
||||
_type = ATIS;
|
||||
}
|
||||
|
||||
FGATIS::~FGATIS() {
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
@ -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?
|
||||
|
|
Loading…
Add table
Reference in a new issue