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,8 +41,12 @@ 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,6 +74,20 @@ 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/");
|
||||
|
@ -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);
|
||||
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,20 +54,33 @@ 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;
|
||||
double lat;
|
||||
|
@ -90,19 +103,35 @@ public:
|
|||
|
||||
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
|
||||
|
||||
// Remove a class from the ai_list and delete it from memory
|
||||
//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);
|
||||
|
||||
};
|
||||
|
||||
#endif // _FG_AIMGR_HXX
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,6 +84,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&
|
||||
|
|
|
@ -397,7 +397,12 @@ void FGATCDialog::PopupCallback() {
|
|||
break;
|
||||
}
|
||||
} else if(atcptr->GetType() == TOWER) {
|
||||
ATCMenuEntry a = ((available_dialog[TOWER])[(string)atcptr->get_ident()])[atcDialogCommunicationOptions->getValue()];
|
||||
//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;
|
||||
|
@ -405,6 +410,10 @@ void FGATCDialog::PopupCallback() {
|
|||
_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) ) {
|
||||
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";
|
||||
(*atc_list_itr)->SetNoDisplay();
|
||||
(*atc_list_itr)->Update(0.00833);
|
||||
delete (*atc_list_itr);
|
||||
atc_list_itr = atc_list.erase(atc_list_itr);
|
||||
(*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;
|
||||
} // Note that that can upset where we are in the list but that doesn't really matter
|
||||
++atc_list_itr;
|
||||
}
|
||||
++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...");
|
||||
//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...");
|
||||
//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
|
||||
|
|
|
@ -43,6 +43,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);
|
||||
lat_node = fgGetNode("/position/latitude-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
|
||||
|
|
|
@ -46,6 +46,7 @@ SG_USING_STD(cout);
|
|||
// TowerPlaneRec
|
||||
|
||||
TowerPlaneRec::TowerPlaneRec() :
|
||||
planePtr(NULL),
|
||||
clearedToLand(false),
|
||||
clearedToLineUp(false),
|
||||
clearedToTakeOff(false),
|
||||
|
@ -71,6 +72,7 @@ TowerPlaneRec::TowerPlaneRec() :
|
|||
}
|
||||
|
||||
TowerPlaneRec::TowerPlaneRec(PlaneRec p) :
|
||||
planePtr(NULL),
|
||||
clearedToLand(false),
|
||||
clearedToLineUp(false),
|
||||
clearedToTakeOff(false),
|
||||
|
@ -96,6 +98,7 @@ TowerPlaneRec::TowerPlaneRec(PlaneRec p) :
|
|||
}
|
||||
|
||||
TowerPlaneRec::TowerPlaneRec(Point3D pt) :
|
||||
planePtr(NULL),
|
||||
clearedToLand(false),
|
||||
clearedToLineUp(false),
|
||||
clearedToTakeOff(false),
|
||||
|
@ -122,6 +125,7 @@ TowerPlaneRec::TowerPlaneRec(Point3D pt) :
|
|||
}
|
||||
|
||||
TowerPlaneRec::TowerPlaneRec(PlaneRec p, Point3D pt) :
|
||||
planePtr(NULL),
|
||||
clearedToLand(false),
|
||||
clearedToLineUp(false),
|
||||
clearedToTakeOff(false),
|
||||
|
@ -153,23 +157,39 @@ TowerPlaneRec::TowerPlaneRec(PlaneRec p, Point3D pt) :
|
|||
/*******************************************
|
||||
TODO List
|
||||
|
||||
Currently user is assumed to have taken off again when leaving the runway - check speed/elev for taxiing-in.
|
||||
Currently user is assumed to have taken off again when leaving the runway - check speed/elev for taxiing-in. (MAJOR)
|
||||
|
||||
Tell AI plane to contact ground when taxiing in.
|
||||
Use track instead of heading to determine what leg of the circuit the user is flying. (MINOR)
|
||||
|
||||
Use track instead of heading to determine what leg of the circuit the user is flying.
|
||||
|
||||
Use altitude as well as position to try to determine if the user has left the circuit.
|
||||
Use altitude as well as position to try to determine if the user has left the circuit. (MEDIUM - other issues as well).
|
||||
|
||||
Currently HoldShortReported code assumes there will be only one plane holding for the runway at once and
|
||||
will break when planes start queueing.
|
||||
will break when planes start queueing. (CRITICAL)
|
||||
|
||||
Implement ReportRunwayVacated
|
||||
Report-Runway-Vacated is left as only user ATC option following a go-around. (MAJOR)
|
||||
|
||||
Report Go-Around should be added to user options following reporting final or downwind. (MEDIUM).
|
||||
|
||||
Report-Downwind is not added as ATC option when user takes off to fly a circuit. (MAJOR)
|
||||
|
||||
eta of USER can be calculated very wrongly in circuit if flying straight out and turn4 etc are with +ve ortho y.
|
||||
This can then screw up circuit ordering for other planes (MEDIUM)
|
||||
|
||||
USER leaving circuit needs to be more robustly considered when intentions unknown
|
||||
Currently only considered during climbout and breaks when user turns (MEDIUM).
|
||||
|
||||
GetPos() of the AI planes is called erratically - either too much or not enough. (MINOR)
|
||||
|
||||
GO-AROUND is instructed very late at < 12s to landing - possibly make more dependent on chance of rwy clearing before landing (FEATURE)
|
||||
|
||||
Need to make clear when TowerPlaneRecs do or don't get deleted in RemoveFromCircuitList etc. (MINOR until I misuse it - then CRITICAL!)
|
||||
*******************************************/
|
||||
|
||||
FGTower::FGTower() {
|
||||
ATCmgr = globals->get_ATC_mgr();
|
||||
|
||||
_type = TOWER;
|
||||
|
||||
// Init the property nodes - TODO - need to make sure we're getting surface winds.
|
||||
wind_from_hdg = fgGetNode("/environment/wind-from-heading-deg", true);
|
||||
wind_speed_knots = fgGetNode("/environment/wind-speed-kt", true);
|
||||
|
@ -178,11 +198,14 @@ FGTower::FGTower() {
|
|||
update_count_max = 15;
|
||||
|
||||
holdListItr = holdList.begin();
|
||||
appList.clear();
|
||||
appListItr = appList.begin();
|
||||
depListItr = depList.begin();
|
||||
rwyListItr = rwyList.begin();
|
||||
circuitListItr = circuitList.begin();
|
||||
trafficListItr = trafficList.begin();
|
||||
vacatedList.clear();
|
||||
vacatedListItr = vacatedList.begin();
|
||||
|
||||
freqClear = true;
|
||||
|
||||
|
@ -192,6 +215,8 @@ FGTower::FGTower() {
|
|||
nominal_downwind_leg_pos = 1000.0;
|
||||
nominal_base_leg_pos = -1000.0;
|
||||
// TODO - set nominal crosswind leg pos based on minimum distance from takeoff end of rwy.
|
||||
|
||||
_departureControlled = false;
|
||||
}
|
||||
|
||||
FGTower::~FGTower() {
|
||||
|
@ -201,6 +226,7 @@ FGTower::~FGTower() {
|
|||
}
|
||||
|
||||
void FGTower::Init() {
|
||||
//cout << "Initialising tower " << ident << '\n';
|
||||
display = false;
|
||||
|
||||
// Pointers to user's position
|
||||
|
@ -257,14 +283,18 @@ void FGTower::Init() {
|
|||
}
|
||||
}
|
||||
|
||||
// Get the airport elevation
|
||||
aptElev = dclGetAirportElev(ident.c_str()) * SG_FEET_TO_METER;
|
||||
// TODO - attempt to get a departure control pointer to see if we need to hand off departing traffic to departure.
|
||||
|
||||
// Get the airport elevation
|
||||
aptElev = dclGetAirportElev(ident.c_str());
|
||||
|
||||
// TODO - this function only assumes one active rwy.
|
||||
DoRwyDetails();
|
||||
|
||||
// FIXME - this currently assumes use of the active rwy by the user.
|
||||
rwyOccupied = OnAnyRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0));
|
||||
// TODO - this currently assumes only one active runway.
|
||||
rwyOccupied = OnActiveRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0));
|
||||
if(rwyOccupied) {
|
||||
//cout << "User found on active runway\n";
|
||||
// Assume the user is started at the threshold ready to take-off
|
||||
TowerPlaneRec* t = new TowerPlaneRec;
|
||||
t->plane.callsign = fgGetString("/sim/user/callsign");
|
||||
|
@ -278,10 +308,13 @@ void FGTower::Init() {
|
|||
rwyList.push_back(t);
|
||||
departed = false;
|
||||
} else {
|
||||
//cout << "User not on active runway\n";
|
||||
// For now assume that this means the user is not at the airport and is in the air.
|
||||
// TODO FIXME - this will break when user starts on apron, at hold short, etc.
|
||||
if(!OnAnyRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0))) {
|
||||
current_atcdialog->add_entry(ident, "@AP Tower @CS @MI miles @CD of the airport for full stop with the ATIS", "Contact tower for VFR arrival (full stop)", TOWER, (int)USER_REQUEST_VFR_ARRIVAL_FULL_STOP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FGTower::Update(double dt) {
|
||||
|
@ -357,6 +390,10 @@ void FGTower::Update(double dt) {
|
|||
CheckApproachList(dt);
|
||||
}
|
||||
|
||||
if(update_count == 8) {
|
||||
CheckDepartureList(dt);
|
||||
}
|
||||
|
||||
// TODO - do one plane from the departure list and set departed = false when out of consideration
|
||||
|
||||
//doCommunication();
|
||||
|
@ -399,7 +436,7 @@ void FGTower::Update(double dt) {
|
|||
|
||||
void FGTower::ReceiveUserCallback(int code) {
|
||||
if(code == (int)USER_REQUEST_VFR_DEPARTURE) {
|
||||
cout << "User requested departure\n";
|
||||
//cout << "User requested departure\n";
|
||||
} else if(code == (int)USER_REQUEST_VFR_ARRIVAL) {
|
||||
VFRArrivalContact("USER");
|
||||
} else if(code == (int)USER_REQUEST_VFR_ARRIVAL_FULL_STOP) {
|
||||
|
@ -417,11 +454,12 @@ void FGTower::ReceiveUserCallback(int code) {
|
|||
}
|
||||
|
||||
void FGTower::Respond() {
|
||||
cout << "Entering Respond, responseID = " << responseID << endl;
|
||||
//cout << "\nEntering Respond, responseID = " << responseID << endl;
|
||||
TowerPlaneRec* t = FindPlane(responseID);
|
||||
if(t) {
|
||||
// This will grow!!!
|
||||
if(t->vfrArrivalReported && !t->vfrArrivalAcknowledged) {
|
||||
//cout << "Tower " << ident << " is responding to VFR arrival reported...\n";
|
||||
// Testing - hardwire straight in for now
|
||||
string trns = t->plane.callsign;
|
||||
trns += " ";
|
||||
|
@ -433,24 +471,37 @@ void FGTower::Respond() {
|
|||
Point3D op = ortho.ConvertToLocal(t->pos);
|
||||
if(op.y() < -1000) {
|
||||
trns += " Report three mile straight-in runway ";
|
||||
t->opType = STRAIGHT_IN;
|
||||
if(t->isUser) {
|
||||
current_atcdialog->add_entry(ident, "@AP Tower @CS @MI mile final Runway @RW", "Report Final", TOWER, (int)USER_REPORT_3_MILE_FINAL);
|
||||
} else {
|
||||
t->planePtr->RegisterTransmission(14);
|
||||
}
|
||||
} else {
|
||||
// For now we'll just request reporting downwind.
|
||||
// TODO - In real life something like 'report 2 miles southwest right downwind rwy 19R' might be used
|
||||
// but I'm not sure how to handle all permutations of which direction to tell to report from yet.
|
||||
trns += " Report ";
|
||||
trns += (rwy.patternDirection ? "right " : "left ");
|
||||
//cout << "Responding, rwy.patterDirection is " << rwy.patternDirection << '\n';
|
||||
trns += ((rwy.patternDirection == 1) ? "right " : "left ");
|
||||
trns += "downwind runway ";
|
||||
t->opType = CIRCUIT;
|
||||
// leave it in the app list until it gets into pattern though.
|
||||
if(t->isUser) {
|
||||
current_atcdialog->add_entry(ident, "@AP Tower @CS Downwind @RW", "Report Downwind", TOWER, (int)USER_REPORT_DOWNWIND);
|
||||
} else {
|
||||
t->planePtr->RegisterTransmission(15);
|
||||
}
|
||||
}
|
||||
trns += ConvertRwyNumToSpokenString(activeRwy);
|
||||
if(display) {
|
||||
globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
|
||||
} else {
|
||||
cout << "Not displaying, trns was " << trns << '\n';
|
||||
//cout << "Not displaying, trns was " << trns << '\n';
|
||||
}
|
||||
t->vfrArrivalAcknowledged = true;
|
||||
} else if(t->downwindReported) {
|
||||
//cout << "Tower " << ident << " is responding to downwind reported...\n";
|
||||
t->downwindReported = false;
|
||||
int i = 1;
|
||||
for(tower_plane_rec_list_iterator twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) {
|
||||
|
@ -461,9 +512,10 @@ void FGTower::Respond() {
|
|||
trns += " Number ";
|
||||
trns += ConvertNumToSpokenDigits(i);
|
||||
trns += " ";
|
||||
if(i == 1) {
|
||||
trns += "Cleared to land";
|
||||
if((i == 1) && (!rwyList.size()) && (t->nextOnRwy)) {
|
||||
trns += "Cleared to land"; // TODO - clear for the option if appropriate
|
||||
t->clearedToLand = true;
|
||||
if(!t->isUser) t->planePtr->RegisterTransmission(7);
|
||||
}
|
||||
if(display) {
|
||||
globals->get_ATC_display()->RegisterSingleMessage(trns);
|
||||
|
@ -471,8 +523,10 @@ void FGTower::Respond() {
|
|||
if(t->isUser) {
|
||||
if(t->opType == TTT_UNKNOWN) t->opType = CIRCUIT;
|
||||
current_atcdialog->add_entry(ident, "@CS Clear of the runway", "Report runway vacated", TOWER, USER_REPORT_RWY_VACATED);
|
||||
// TODO - add report going around as an option
|
||||
}
|
||||
} else if(t->holdShortReported) {
|
||||
//cout << "Tower " << ident << " is reponding to holdShortReported...\n";
|
||||
if(t->nextOnRwy) {
|
||||
if(rwyOccupied) { // TODO - ought to add a sanity check that it isn't this plane only on the runway (even though it shouldn't be!!)
|
||||
// Do nothing for now - consider acknowloging hold short eventually
|
||||
|
@ -503,11 +557,12 @@ void FGTower::Respond() {
|
|||
}
|
||||
t->holdShortReported = false;
|
||||
} else if(t->finalReported && !(t->finalAcknowledged)) {
|
||||
//cout << "Tower " << ident << " is responding to finalReported...\n";
|
||||
bool disp = true;
|
||||
string trns = t->plane.callsign;
|
||||
cout << (t->nextOnRwy ? "Next on rwy " : "Not next!! ");
|
||||
cout << (rwyOccupied ? "RWY OCCUPIED!!\n" : "Rwy not occupied\n");
|
||||
if(t->nextOnRwy && !rwyOccupied) {
|
||||
//cout << (t->nextOnRwy ? "Next on rwy " : "Not next!! ");
|
||||
//cout << (rwyOccupied ? "RWY OCCUPIED!!\n" : "Rwy not occupied\n");
|
||||
if(t->nextOnRwy && !rwyOccupied && !(t->instructedToGoAround)) {
|
||||
if(t->landingType == FULL_STOP) {
|
||||
trns += " cleared to land ";
|
||||
} else {
|
||||
|
@ -515,7 +570,13 @@ void FGTower::Respond() {
|
|||
}
|
||||
// TODO - add winds
|
||||
t->clearedToLand = true;
|
||||
if(t->isUser) current_atcdialog->add_entry(ident, "@CS Clear of the runway", "Report runway vacated", TOWER, USER_REPORT_RWY_VACATED);
|
||||
// Maybe remove report downwind from menu here as well incase user didn't bother to?
|
||||
if(t->isUser) {
|
||||
current_atcdialog->add_entry(ident, "@CS Clear of the runway", "Report runway vacated", TOWER, USER_REPORT_RWY_VACATED);
|
||||
// TODO - add report going around as an option.
|
||||
} else {
|
||||
t->planePtr->RegisterTransmission(7);
|
||||
}
|
||||
} else if(t->eta < 20) {
|
||||
// Do nothing - we'll be telling it to go around in less than 10 seconds if the
|
||||
// runway doesn't clear so no point in calling "continue approach".
|
||||
|
@ -529,6 +590,19 @@ void FGTower::Respond() {
|
|||
}
|
||||
t->finalAcknowledged = true;
|
||||
} else if(t->rwyVacatedReported && !(t->rwyVacatedAcknowledged)) {
|
||||
ProcessRunwayVacatedReport(t);
|
||||
t->rwyVacatedAcknowledged = true;
|
||||
}
|
||||
}
|
||||
//freqClear = true; // FIXME - set this to come true after enough time to render the message
|
||||
_releaseCounter = 0.0;
|
||||
_releaseTime = 5.5;
|
||||
_runReleaseCounter = true;
|
||||
//cout << "Done Respond\n" << endl;
|
||||
}
|
||||
|
||||
void FGTower::ProcessRunwayVacatedReport(TowerPlaneRec* t) {
|
||||
//cout << "Processing rwy vacated...\n";
|
||||
string trns = t->plane.callsign;
|
||||
if(separateGround) {
|
||||
trns += " Contact ground on ";
|
||||
|
@ -537,19 +611,17 @@ void FGTower::Respond() {
|
|||
sprintf(buf, "%.2f", f);
|
||||
trns += buf;
|
||||
trns += " Good Day";
|
||||
if(!t->isUser) t->planePtr->RegisterTransmission(5);
|
||||
} else {
|
||||
// Cop-out!!
|
||||
trns += " cleared for taxi to the GA parking";
|
||||
if(!t->isUser) t->planePtr->RegisterTransmission(6); // TODO - this is a mega-hack!!
|
||||
}
|
||||
//cout << "trns = " << trns << '\n';
|
||||
if(display) {
|
||||
globals->get_ATC_display()->RegisterSingleMessage(trns);
|
||||
}
|
||||
t->rwyVacatedAcknowledged = true;
|
||||
// Maybe we should check that the plane really *has* vacated the runway!
|
||||
}
|
||||
}
|
||||
freqClear = true; // FIXME - set this to come true after enough time to render the message
|
||||
//cout << "Done Respond" << endl;
|
||||
}
|
||||
|
||||
// Currently this assumes we *are* next on the runway and doesn't check for planes about to land -
|
||||
|
@ -659,6 +731,10 @@ void FGTower::ClearHoldingPlane(TowerPlaneRec* t) {
|
|||
//cout << "Done ClearHoldingPlane " << endl;
|
||||
}
|
||||
|
||||
|
||||
// ***************************************************************************************
|
||||
// ********** Functions to periodically check what the various traffic is doing **********
|
||||
|
||||
// Do one plane from the hold list
|
||||
void FGTower::CheckHoldList(double dt) {
|
||||
//cout << "Entering CheckHoldList..." << endl;
|
||||
|
@ -710,10 +786,12 @@ void FGTower::CheckCircuitList(double dt) {
|
|||
circuitListItr = circuitList.begin();
|
||||
}
|
||||
TowerPlaneRec* t = *circuitListItr;
|
||||
//cout << ident << ' ' << circuitList.size() << ' ' << t->plane.callsign << " " << t->leg << " eta " << t->eta << '\n';
|
||||
if(t->isUser) {
|
||||
t->pos.setlon(user_lon_node->getDoubleValue());
|
||||
t->pos.setlat(user_lat_node->getDoubleValue());
|
||||
t->pos.setelev(user_elev_node->getDoubleValue());
|
||||
//cout << ident << ' ' << circuitList.size() << ' ' << t->plane.callsign << " " << t->leg << " eta " << t->eta << '\n';
|
||||
} else {
|
||||
t->pos = t->planePtr->GetPos(); // We should probably only set the pos's on one walk through the traffic list in the update function, to save a few CPU should we end up duplicating this.
|
||||
t->landingType = t->planePtr->GetLandingOption();
|
||||
|
@ -738,11 +816,13 @@ void FGTower::CheckCircuitList(double dt) {
|
|||
//cout << "Climbout\n";
|
||||
// If it's the user we may be unsure of his/her intentions.
|
||||
// (Hopefully the AI planes won't try confusing the sim!!!)
|
||||
//cout << "tortho.y = " << tortho.y() << '\n';
|
||||
if(t->opType == TTT_UNKNOWN) {
|
||||
if(tortho.y() > 5000) {
|
||||
// 5 km out from threshold - assume it's a departure
|
||||
t->opType = OUTBOUND; // TODO - could check if the user has climbed significantly above circuit altitude as well.
|
||||
// Since we are unknown operation we should be in depList already.
|
||||
//cout << ident << " Removing user from circuitList (TTT_UNKNOWN)\n";
|
||||
circuitList.erase(circuitListItr);
|
||||
RemoveFromTrafficList(t->plane.callsign);
|
||||
circuitListItr = circuitList.begin();
|
||||
|
@ -752,6 +832,7 @@ void FGTower::CheckCircuitList(double dt) {
|
|||
// 10 km out - assume the user has abandoned the circuit!!
|
||||
t->opType = OUTBOUND;
|
||||
depList.push_back(t);
|
||||
//cout << ident << " removing user from circuitList (CIRCUIT)\n";
|
||||
circuitList.erase(circuitListItr);
|
||||
circuitListItr = circuitList.begin();
|
||||
}
|
||||
|
@ -852,9 +933,12 @@ void FGTower::CheckCircuitList(double dt) {
|
|||
}
|
||||
}
|
||||
|
||||
if(t->leg == FINAL) {
|
||||
if(t->landingType == FULL_STOP) t->opType = INBOUND;
|
||||
if(t->eta < 12 && rwyList.size() && !(t->instructedToGoAround)) {
|
||||
if(t->leg == FINAL && !(t->instructedToGoAround)) {
|
||||
//cout << "YES FINAL, t->eta = " << t->eta << ", rwyList.size() = " << rwyList.size() << '\n';
|
||||
if(t->landingType == FULL_STOP) {
|
||||
t->opType = INBOUND;
|
||||
//cout << "\n******** SWITCHING TO INBOUND AT POINT AAA *********\n\n";
|
||||
if(t->eta < 12 && rwyList.size()) {
|
||||
// TODO - need to make this more sophisticated
|
||||
// eg. is the plane accelerating down the runway taking off [OK],
|
||||
// or stationary near the start [V. BAD!!].
|
||||
|
@ -865,12 +949,18 @@ void FGTower::CheckCircuitList(double dt) {
|
|||
globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
|
||||
}
|
||||
t->instructedToGoAround = true;
|
||||
t->clearedToLand = false;
|
||||
// Assume it complies!!!
|
||||
t->opType = CIRCUIT;
|
||||
t->leg = CLIMBOUT;
|
||||
if(t->planePtr) {
|
||||
cout << "Registering Go-around transmission with AI plane\n";
|
||||
//cout << "Registering Go-around transmission with AI plane\n";
|
||||
t->planePtr->RegisterTransmission(13);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(t->leg == LANDING_ROLL) {
|
||||
//cout << t->plane.callsign << " has landed - adding to rwyList\n";
|
||||
rwyList.push_front(t);
|
||||
// TODO - if(!clearedToLand) shout something!!
|
||||
t->clearedToLand = false;
|
||||
|
@ -878,6 +968,7 @@ void FGTower::CheckCircuitList(double dt) {
|
|||
if(t->isUser) {
|
||||
t->opType = TTT_UNKNOWN;
|
||||
} // TODO - allow the user to specify opType via ATC menu
|
||||
//cout << ident << " Removing " << t->plane.callsign << " from circuitList..." << endl;
|
||||
circuitListItr = circuitList.erase(circuitListItr);
|
||||
if(circuitListItr == circuitList.end() ) {
|
||||
circuitListItr = circuitList.begin();
|
||||
|
@ -909,16 +1000,24 @@ void FGTower::CheckRunwayList(double dt) {
|
|||
}
|
||||
bool on_rwy = OnActiveRunway(t->pos);
|
||||
if(!on_rwy) {
|
||||
// TODO - for all of these we need to check what the user is *actually* doing!
|
||||
if((t->opType == INBOUND) || (t->opType == STRAIGHT_IN)) {
|
||||
rwyList.pop_front();
|
||||
delete t;
|
||||
// TODO - tell it to taxi / contact ground / don't delete it etc!
|
||||
//cout << "Tower " << ident << " is removing plane " << t->plane.callsign << " from rwy list (vacated)\n";
|
||||
//cout << "Size of rwylist was " << rwyList.size() << '\n';
|
||||
//cout << "Size of vacatedList was " << vacatedList.size() << '\n';
|
||||
RemoveFromRwyList(t->plane.callsign);
|
||||
vacatedList.push_back(t);
|
||||
//cout << "Size of rwylist is " << rwyList.size() << '\n';
|
||||
//cout << "Size of vacatedList is " << vacatedList.size() << '\n';
|
||||
// At the moment we wait until Runway Vacated is reported by the plane before telling to contact ground etc.
|
||||
// It's possible we could be a bit more proactive about this.
|
||||
} else if(t->opType == OUTBOUND) {
|
||||
depList.push_back(t);
|
||||
rwyList.pop_front();
|
||||
departed = true;
|
||||
timeSinceLastDeparture = 0.0;
|
||||
} else if(t->opType == CIRCUIT) {
|
||||
//cout << ident << " adding " << t->plane.callsign << " to circuitList" << endl;
|
||||
circuitList.push_back(t);
|
||||
AddToTrafficList(t);
|
||||
rwyList.pop_front();
|
||||
|
@ -926,6 +1025,7 @@ void FGTower::CheckRunwayList(double dt) {
|
|||
timeSinceLastDeparture = 0.0;
|
||||
} else if(t->opType == TTT_UNKNOWN) {
|
||||
depList.push_back(t);
|
||||
//cout << ident << " adding " << t->plane.callsign << " to circuitList" << endl;
|
||||
circuitList.push_back(t);
|
||||
AddToTrafficList(t);
|
||||
rwyList.pop_front();
|
||||
|
@ -942,12 +1042,15 @@ void FGTower::CheckRunwayList(double dt) {
|
|||
|
||||
// Do one plane from the approach list
|
||||
void FGTower::CheckApproachList(double dt) {
|
||||
//cout << "CheckApproachList called for " << ident << endl;
|
||||
//cout << "AppList.size is " << appList.size() << endl;
|
||||
if(appList.size()) {
|
||||
if(appListItr == appList.end()) {
|
||||
appListItr = appList.begin();
|
||||
}
|
||||
TowerPlaneRec* t = *appListItr;
|
||||
//cout << "t = " << t << endl;
|
||||
//cout << "Checking " << t->plane.callsign << endl;
|
||||
if(t->isUser) {
|
||||
t->pos.setlon(user_lon_node->getDoubleValue());
|
||||
t->pos.setlat(user_lat_node->getDoubleValue());
|
||||
|
@ -955,12 +1058,123 @@ void FGTower::CheckApproachList(double dt) {
|
|||
} else {
|
||||
// TODO - set/update the position if it's an AI plane
|
||||
}
|
||||
if(t->nextOnRwy && !(t->clearedToLand)) {
|
||||
//cout << "eta is " << t->eta << ", rwy is " << (rwyList.size() ? "occupied " : "clear ") << '\n';
|
||||
if(t->eta < 12 && rwyList.size() && !(t->instructedToGoAround)) {
|
||||
// TODO - need to make this more sophisticated
|
||||
// eg. is the plane accelerating down the runway taking off [OK],
|
||||
// or stationary near the start [V. BAD!!].
|
||||
// For now this should stop the AI plane landing on top of the user.
|
||||
string trns = t->plane.callsign;
|
||||
trns += " GO AROUND TRAFFIC ON RUNWAY I REPEAT GO AROUND";
|
||||
if(display) {
|
||||
globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
|
||||
}
|
||||
t->instructedToGoAround = true;
|
||||
t->clearedToLand = false;
|
||||
t->nextOnRwy = false; // But note this is recalculated so don't rely on it
|
||||
// Assume it complies!!!
|
||||
t->opType = CIRCUIT;
|
||||
t->leg = CLIMBOUT;
|
||||
if(!t->isUser) {
|
||||
if(t->planePtr) {
|
||||
//cout << "Registering Go-around transmission with AI plane\n";
|
||||
t->planePtr->RegisterTransmission(13);
|
||||
}
|
||||
} else {
|
||||
// TODO - add Go-around ack to comm options,
|
||||
// remove report rwy vacated. (possibly).
|
||||
}
|
||||
}
|
||||
if(t->nextOnRwy && !(t->clearedToLand) && !(t->instructedToGoAround)) {
|
||||
// check distance away and whether runway occupied
|
||||
// and schedule transmission if necessary
|
||||
}
|
||||
|
||||
// Check for landing...
|
||||
bool landed = false;
|
||||
if(!t->isUser) {
|
||||
if(t->planePtr) {
|
||||
if(t->planePtr->GetLeg() == LANDING_ROLL) {
|
||||
landed = true;
|
||||
}
|
||||
} else {
|
||||
SG_LOG(SG_ATC, SG_ALERT, "WARNING - not user and null planePtr in CheckApproachList!");
|
||||
}
|
||||
} else { // user
|
||||
if(OnActiveRunway(t->pos)) {
|
||||
landed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(landed) {
|
||||
// Duplicated in CheckCircuitList - must be able to rationalise this somehow!
|
||||
//cout << "A " << t->plane.callsign << " has landed, adding to rwyList...\n";
|
||||
rwyList.push_front(t);
|
||||
// TODO - if(!clearedToLand) shout something!!
|
||||
t->clearedToLand = false;
|
||||
RemoveFromTrafficList(t->plane.callsign);
|
||||
//if(t->isUser) {
|
||||
// t->opType = TTT_UNKNOWN;
|
||||
//} // TODO - allow the user to specify opType via ATC menu
|
||||
appListItr = appList.erase(appListItr);
|
||||
if(appListItr == appList.end() ) {
|
||||
appListItr = appList.begin();
|
||||
}
|
||||
}
|
||||
|
||||
++appListItr;
|
||||
}
|
||||
//cout << "Done" << endl;
|
||||
}
|
||||
|
||||
// Do one plane from the departure list
|
||||
void FGTower::CheckDepartureList(double dt) {
|
||||
if(depList.size()) {
|
||||
if(depListItr == depList.end()) {
|
||||
depListItr = depList.begin();
|
||||
}
|
||||
TowerPlaneRec* t = *depListItr;
|
||||
//cout << "Dep list, checking " << t->plane.callsign;
|
||||
|
||||
double distout; // meters
|
||||
if(t->isUser) distout = dclGetHorizontalSeparation(Point3D(lon, lat, elev), Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue()));
|
||||
else distout = dclGetHorizontalSeparation(Point3D(lon, lat, elev), t->planePtr->GetPos());
|
||||
//cout << " distout = " << distout << '\n';
|
||||
if(distout > 10000) {
|
||||
string trns = t->plane.callsign;
|
||||
trns += " You are now clear of my airspace, good day";
|
||||
if(display) {
|
||||
globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
|
||||
}
|
||||
if(t->isUser) {
|
||||
// Change the communication options
|
||||
RemoveAllUserDialogOptions();
|
||||
current_atcdialog->add_entry(ident, "@AP Tower @CS @MI miles @CD of the airport for full stop with the ATIS", "Contact tower for VFR arrival (full stop)", TOWER, (int)USER_REQUEST_VFR_ARRIVAL_FULL_STOP);
|
||||
} else {
|
||||
// Send a clear-of-airspace signal
|
||||
// TODO - implement this once we actually have departing AI traffic (currently all circuits or arrivals).
|
||||
}
|
||||
RemovePlane(t->plane.callsign);
|
||||
} else {
|
||||
++depListItr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ********** End periodic check functions ***********************************************
|
||||
// ***************************************************************************************
|
||||
|
||||
|
||||
// Remove all dialog options for this tower.
|
||||
void FGTower::RemoveAllUserDialogOptions() {
|
||||
current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_DEPARTURE, TOWER);
|
||||
current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_ARRIVAL, TOWER);
|
||||
current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_ARRIVAL_FULL_STOP, TOWER);
|
||||
current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_ARRIVAL_TOUCH_AND_GO, TOWER);
|
||||
current_atcdialog->remove_entry(ident, USER_REPORT_3_MILE_FINAL, TOWER);
|
||||
current_atcdialog->remove_entry(ident, USER_REPORT_DOWNWIND, TOWER);
|
||||
current_atcdialog->remove_entry(ident, USER_REPORT_RWY_VACATED, TOWER);
|
||||
current_atcdialog->remove_entry(ident, USER_REPORT_GOING_AROUND, TOWER);
|
||||
}
|
||||
|
||||
// Returns true if positions of crosswind/downwind/base leg turns should be constrained by previous traffic
|
||||
|
@ -1011,6 +1225,7 @@ void FGTower::DoRwyDetails() {
|
|||
FGRunway runway;
|
||||
bool rwyGood = globals->get_runways()->search(ident, int(hdg), &runway);
|
||||
if(rwyGood) {
|
||||
//cout << "RUNWAY GOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOD\n";
|
||||
activeRwy = runway.rwy_no;
|
||||
rwy.rwyID = runway.rwy_no;
|
||||
SG_LOG(SG_ATC, SG_INFO, "Active runway for airport " << ident << " is " << activeRwy);
|
||||
|
@ -1051,6 +1266,7 @@ void FGTower::DoRwyDetails() {
|
|||
if(rwy.rwyID.size() == 3) {
|
||||
rwy.patternDirection = (rwy.rwyID.substr(2,1) == "R" ? 1 : -1);
|
||||
}
|
||||
//cout << "Doing details, rwy.patterDirection is " << rwy.patternDirection << '\n';
|
||||
} else {
|
||||
SG_LOG(SG_ATC, SG_ALERT, "Help - can't get good runway in FGTower!!");
|
||||
activeRwy = "NN";
|
||||
|
@ -1113,6 +1329,7 @@ bool FGTower::RemoveFromTrafficList(string id) {
|
|||
TowerPlaneRec* tpr = *twrItr;
|
||||
if(tpr->plane.callsign == id) {
|
||||
trafficList.erase(twrItr);
|
||||
trafficListItr = trafficList.begin();
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
|
@ -1121,6 +1338,37 @@ bool FGTower::RemoveFromTrafficList(string id) {
|
|||
}
|
||||
|
||||
|
||||
// Returns true if successful
|
||||
bool FGTower::RemoveFromAppList(string id) {
|
||||
tower_plane_rec_list_iterator twrItr;
|
||||
for(twrItr = appList.begin(); twrItr != appList.end(); twrItr++) {
|
||||
TowerPlaneRec* tpr = *twrItr;
|
||||
if(tpr->plane.callsign == id) {
|
||||
appList.erase(twrItr);
|
||||
appListItr = appList.begin();
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
//SG_LOG(SG_ATC, SG_WARN, "Warning - unable to remove aircraft " << id << " from appList in FGTower");
|
||||
return(false);
|
||||
}
|
||||
|
||||
// Returns true if successful
|
||||
bool FGTower::RemoveFromRwyList(string id) {
|
||||
tower_plane_rec_list_iterator twrItr;
|
||||
for(twrItr = rwyList.begin(); twrItr != rwyList.end(); twrItr++) {
|
||||
TowerPlaneRec* tpr = *twrItr;
|
||||
if(tpr->plane.callsign == id) {
|
||||
rwyList.erase(twrItr);
|
||||
rwyListItr = rwyList.begin();
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
//SG_LOG(SG_ATC, SG_WARN, "Warning - unable to remove aircraft " << id << " from rwyList in FGTower");
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
// Add a tower plane rec with ETA to the traffic list in the correct position ETA-wise
|
||||
// and set nextOnRwy if so.
|
||||
// Returns true if this could cause a threshold ETA conflict with other traffic, false otherwise.
|
||||
|
@ -1198,15 +1446,35 @@ bool FGTower::AddToTrafficList(TowerPlaneRec* t, bool holding) {
|
|||
|
||||
// Add a tower plane rec with ETA to the circuit list in the correct position ETA-wise
|
||||
// Returns true if this might cause a separation conflict (based on ETA) with other traffic, false otherwise.
|
||||
// Safe to add a plane that is already in - planes with the same callsign are not added.
|
||||
bool FGTower::AddToCircuitList(TowerPlaneRec* t) {
|
||||
if(!t) {
|
||||
//cout << "**********************************************\n";
|
||||
//cout << "AddToCircuitList called with NULL pointer!!!!!\n";
|
||||
//cout << "**********************************************\n";
|
||||
return false;
|
||||
}
|
||||
//cout << "ADD: " << circuitList.size();
|
||||
//cout << "AddToCircuitList called, currently size = " << circuitList.size() << endl;
|
||||
//cout << ident << " AddToCircuitList called for " << t->plane.callsign << ", currently size = " << circuitList.size() << endl;
|
||||
double separation_time = 60.0; // seconds - this is currently a guess for light plane separation, and includes a few seconds for a holding plane to taxi onto the rwy.
|
||||
bool conflict = false;
|
||||
tower_plane_rec_list_iterator twrItr;
|
||||
// First check if the plane is already in the list
|
||||
//cout << "A" << endl;
|
||||
//cout << "Checking whether " << t->plane.callsign << " is already in circuit list..." << endl;
|
||||
//cout << "B" << endl;
|
||||
for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) {
|
||||
if((*twrItr)->plane.callsign == t->plane.callsign) {
|
||||
//cout << "In list - returning...\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//cout << "Not in list - adding..." << endl;
|
||||
|
||||
for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) {
|
||||
TowerPlaneRec* tpr = *twrItr;
|
||||
|
||||
//cout << tpr->plane.callsign << " eta is " << tpr->eta << '\n';
|
||||
//cout << "New eta is " << t->eta << '\n';
|
||||
if(t->eta < tpr->eta) {
|
||||
// Ugg - this one's tricky.
|
||||
// It depends on what the two planes are doing and whether there's a conflict what we do.
|
||||
|
@ -1222,6 +1490,7 @@ bool FGTower::AddToCircuitList(TowerPlaneRec* t) {
|
|||
}
|
||||
}
|
||||
// If we get here we must be at the end of the list, or maybe the list is empty.
|
||||
//cout << ident << " adding " << t->plane.callsign << " to circuitList" << endl;
|
||||
circuitList.push_back(t); // TODO - check the separation with the preceding plane for the conflict flag.
|
||||
//cout << "\tE\t" << circuitList.size() << endl;
|
||||
return(conflict);
|
||||
|
@ -1250,13 +1519,14 @@ void FGTower::CalcETA(TowerPlaneRec* tpr, bool printout) {
|
|||
Point3D op = ortho.ConvertToLocal(tpr->pos);
|
||||
//if(printout) {
|
||||
//if(!tpr->isUser) cout << "Orthopos is " << op.x() << ", " << op.y() << ' ';
|
||||
// cout << "opType is " << tpr->opType << '\n';
|
||||
//cout << "opType is " << tpr->opType << '\n';
|
||||
//}
|
||||
double dist_out_m = op.y();
|
||||
double dist_across_m = fabs(op.x()); // FIXME = the fabs is a hack to cope with the fact that we don't know the circuit direction yet
|
||||
double dist_across_m = fabs(op.x()); // The fabs is a hack to cope with the fact that we don't know the circuit direction yet
|
||||
//cout << "Doing ETA calc for " << tpr->plane.callsign << '\n';
|
||||
|
||||
if(tpr->opType == STRAIGHT_IN) {
|
||||
if(tpr->opType == STRAIGHT_IN || tpr->opType == INBOUND) {
|
||||
//cout << "CASE 1\n";
|
||||
double dist_to_go_m = sqrt((dist_out_m * dist_out_m) + (dist_across_m * dist_across_m));
|
||||
if(dist_to_go_m < 1000) {
|
||||
tpr->eta = dist_to_go_m / final_ias;
|
||||
|
@ -1264,13 +1534,15 @@ void FGTower::CalcETA(TowerPlaneRec* tpr, bool printout) {
|
|||
tpr->eta = (1000.0 / final_ias) + ((dist_to_go_m - 1000.0) / app_ias);
|
||||
}
|
||||
} else if(tpr->opType == CIRCUIT || tpr->opType == TTT_UNKNOWN) { // Hack alert - UNKNOWN has sort of been added here as a temporary hack.
|
||||
//cout << "CASE 2\n";
|
||||
// It's complicated - depends on if base leg is delayed or not
|
||||
//if(printout) {
|
||||
// cout << "Leg = " << tpr->leg << '\n';
|
||||
//cout << "Leg = " << tpr->leg << '\n';
|
||||
//}
|
||||
if(tpr->leg == LANDING_ROLL) {
|
||||
tpr->eta = 0;
|
||||
} else if((tpr->leg == FINAL) || (tpr->leg == TURN4)) {
|
||||
//cout << "dist_out_m = " << dist_out_m << '\n';
|
||||
tpr->eta = fabs(dist_out_m) / final_ias;
|
||||
} else if((tpr->leg == BASE) || (tpr->leg == TURN3)) {
|
||||
tpr->eta = (fabs(dist_out_m) / final_ias) + (dist_across_m / circuit_ias);
|
||||
|
@ -1289,7 +1561,7 @@ void FGTower::CalcETA(TowerPlaneRec* tpr, bool printout) {
|
|||
if(!GetDownwindConstraint(current_dist_across_m)) {
|
||||
current_dist_across_m = nominal_dist_across_m;
|
||||
}
|
||||
double nominal_cross_dist_out_m = 2000; // Bit of a guess - AI plane turns to crosswind at 600ft agl.
|
||||
double nominal_cross_dist_out_m = 2000; // Bit of a guess - AI plane turns to crosswind at 700ft agl.
|
||||
tpr->eta = fabs(current_base_dist_out_m) / final_ias; // final
|
||||
//cout << "a = " << tpr->eta << '\n';
|
||||
if((tpr->leg == DOWNWIND) || (tpr->leg == TURN2)) {
|
||||
|
@ -1298,24 +1570,35 @@ void FGTower::CalcETA(TowerPlaneRec* tpr, bool printout) {
|
|||
tpr->eta += fabs(current_base_dist_out_m - dist_out_m) / circuit_ias;
|
||||
//cout << "c = " << tpr->eta << '\n';
|
||||
} else if((tpr->leg == CROSSWIND) || (tpr->leg == TURN1)) {
|
||||
//cout << "CROSSWIND calc: ";
|
||||
//cout << tpr->eta << ' ';
|
||||
if(dist_across_m > nominal_dist_across_m) {
|
||||
tpr->eta += dist_across_m / circuit_ias;
|
||||
//cout << "a ";
|
||||
} else {
|
||||
tpr->eta += nominal_dist_across_m / circuit_ias;
|
||||
//cout << "b ";
|
||||
}
|
||||
//cout << tpr->eta << ' ';
|
||||
// should we use the dist across of the previous plane if there is previous still on downwind?
|
||||
//if(printout) cout << "bb = " << tpr->eta << '\n';
|
||||
if(dist_out_m > nominal_cross_dist_out_m) {
|
||||
tpr->eta += fabs(current_base_dist_out_m - dist_out_m) / circuit_ias;
|
||||
//cout << "c ";
|
||||
} else {
|
||||
tpr->eta += fabs(current_base_dist_out_m - nominal_cross_dist_out_m) / circuit_ias;
|
||||
//cout << "d ";
|
||||
}
|
||||
//cout << tpr->eta << ' ';
|
||||
//if(printout) cout << "cc = " << tpr->eta << '\n';
|
||||
if(nominal_dist_across_m > dist_across_m) {
|
||||
tpr->eta += (nominal_dist_across_m - dist_across_m) / circuit_ias;
|
||||
//cout << "e ";
|
||||
} else {
|
||||
// Nothing to add
|
||||
//cout << "f ";
|
||||
}
|
||||
//cout << tpr->eta << '\n';
|
||||
//if(printout) cout << "dd = " << tpr->eta << '\n';
|
||||
} else {
|
||||
// We've only just started - why not use a generic estimate?
|
||||
|
@ -1346,18 +1629,23 @@ double FGTower::CalcDistOutMiles(TowerPlaneRec* tpr) {
|
|||
}
|
||||
|
||||
|
||||
// Iterate through all the lists and call CalcETA for all the planes.
|
||||
// Iterate through all the lists, update the position of, and call CalcETA for all the planes.
|
||||
void FGTower::doThresholdETACalc() {
|
||||
//cout << "Entering doThresholdETACalc..." << endl;
|
||||
tower_plane_rec_list_iterator twrItr;
|
||||
// Do the approach list first
|
||||
for(twrItr = appList.begin(); twrItr != appList.end(); twrItr++) {
|
||||
TowerPlaneRec* tpr = *twrItr;
|
||||
if(!(tpr->isUser)) tpr->pos = tpr->planePtr->GetPos();
|
||||
//cout << "APP: ";
|
||||
CalcETA(tpr);
|
||||
}
|
||||
// Then the circuit list
|
||||
//cout << "Circuit list size is " << circuitList.size() << '\n';
|
||||
for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) {
|
||||
TowerPlaneRec* tpr = *twrItr;
|
||||
if(!(tpr->isUser)) tpr->pos = tpr->planePtr->GetPos();
|
||||
//cout << "CIRC: ";
|
||||
CalcETA(tpr);
|
||||
}
|
||||
//cout << "Done doThresholdETCCalc" << endl;
|
||||
|
@ -1480,7 +1768,9 @@ void FGTower::RegisterAIPlane(PlaneRec plane, FGAIPlane* ai, tower_traffic_type
|
|||
CalcETA(t);
|
||||
|
||||
if(op == CIRCUIT && lg != LEG_UNKNOWN) {
|
||||
cout << "AAAAAAAAAAAAAAAaa" << endl;
|
||||
AddToCircuitList(t);
|
||||
cout << "BBBBBBBBBBBBBBbbb" << endl;
|
||||
} else {
|
||||
// FLAG A WARNING
|
||||
}
|
||||
|
@ -1488,12 +1778,16 @@ void FGTower::RegisterAIPlane(PlaneRec plane, FGAIPlane* ai, tower_traffic_type
|
|||
doThresholdUseOrder();
|
||||
}
|
||||
|
||||
void FGTower::DeregisterAIPlane(string id) {
|
||||
RemovePlane(id);
|
||||
}
|
||||
|
||||
// Contact tower for VFR approach
|
||||
// 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.
|
||||
// opt defaults to AIP_LT_UNKNOWN
|
||||
void FGTower::VFRArrivalContact(string ID, LandingType opt) {
|
||||
//cout << "Request Landing Clearance called...\n";
|
||||
//cout << "USER Request Landing Clearance called for ID " << ID << '\n';
|
||||
|
||||
// For now we'll assume that the user is a light plane and can get him/her to join the circuit if necessary.
|
||||
|
||||
|
@ -1542,11 +1836,38 @@ void FGTower::VFRArrivalContact(string ID, LandingType opt) {
|
|||
current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_ARRIVAL_TOUCH_AND_GO, TOWER);
|
||||
}
|
||||
|
||||
// landingType defaults to AIP_LT_UNKNOWN
|
||||
void FGTower::VFRArrivalContact(PlaneRec plane, FGAIPlane* requestee, LandingType lt) {
|
||||
//cout << "VFRArrivalContact called for plane " << plane.callsign << " at " << ident << '\n';
|
||||
// Possible hack - assume this plane is new for now - TODO - should check really
|
||||
TowerPlaneRec* t = new TowerPlaneRec;
|
||||
t->plane = plane;
|
||||
t->planePtr = requestee;
|
||||
t->landingType = lt;
|
||||
t->pos = requestee->GetPos();
|
||||
|
||||
//cout << "Hold Short reported by " << plane.callsign << '\n';
|
||||
SG_LOG(SG_ATC, SG_BULK, "VFR arrival contact made by " << plane.callsign);
|
||||
//cout << "VFR arrival contact made by " << plane.callsign << '\n';
|
||||
|
||||
// HACK - to get up and running I'm going to assume a staight-in final for now.
|
||||
t->opType = STRAIGHT_IN;
|
||||
|
||||
t->vfrArrivalReported = true;
|
||||
responseReqd = true;
|
||||
|
||||
//cout << "Before adding, appList.size = " << appList.size() << " at " << ident << '\n';
|
||||
appList.push_back(t); // Not necessarily permanent
|
||||
//cout << "After adding, appList.size = " << appList.size() << " at " << ident << '\n';
|
||||
AddToTrafficList(t);
|
||||
}
|
||||
|
||||
void FGTower::RequestDepartureClearance(string ID) {
|
||||
//cout << "Request Departure Clearance called...\n";
|
||||
}
|
||||
|
||||
void FGTower::ReportFinal(string ID) {
|
||||
//cout << "Report Final Called at tower " << ident << " by plane " << ID << '\n';
|
||||
if(ID == "USER") {
|
||||
ID = fgGetString("/sim/user/callsign");
|
||||
current_atcdialog->remove_entry(ident, USER_REPORT_3_MILE_FINAL, TOWER);
|
||||
|
@ -1557,7 +1878,12 @@ void FGTower::ReportFinal(string ID) {
|
|||
t->finalAcknowledged = false;
|
||||
if(!(t->clearedToLand)) {
|
||||
responseReqd = true;
|
||||
} // possibly respond with wind even if already cleared to land?
|
||||
} else {
|
||||
// possibly respond with wind even if already cleared to land?
|
||||
t->finalReported = false;
|
||||
t->finalAcknowledged = true;
|
||||
// HACK!! - prevents next reporting being misinterpreted as this one.
|
||||
}
|
||||
} else {
|
||||
SG_LOG(SG_ATC, SG_WARN, "WARNING: Unable to find plane " << ID << " in FGTower::ReportFinal(...)");
|
||||
}
|
||||
|
@ -1586,38 +1912,115 @@ void FGTower::ReportLongFinal(string ID) {
|
|||
//void FGTower::ReportGoingAround(string ID);
|
||||
|
||||
void FGTower::ReportRunwayVacated(string ID) {
|
||||
//cout << "Report Runway Vacated Called...\n";
|
||||
//cout << "Report Runway Vacated Called at tower " << ident << " by plane " << ID << '\n';
|
||||
if(ID == "USER") {
|
||||
ID = fgGetString("/sim/user/callsign");
|
||||
current_atcdialog->remove_entry(ident, USER_REPORT_RWY_VACATED, TOWER);
|
||||
}
|
||||
TowerPlaneRec* t = FindPlane(ID);
|
||||
if(t) {
|
||||
//cout << "Found it...\n";
|
||||
t->rwyVacatedReported = true;
|
||||
responseReqd = true;
|
||||
} else {
|
||||
SG_LOG(SG_ATC, SG_WARN, "WARNING: Unable to find plane " << ID << " in FGTower::ReportRunwayVacated(...)");
|
||||
SG_LOG(SG_ATC, SG_ALERT, "A WARNING: Unable to find plane " << ID << " in FGTower::ReportRunwayVacated(...)");
|
||||
//cout << "WARNING: Unable to find plane " << ID << " in FGTower::ReportRunwayVacated(...)\n";
|
||||
}
|
||||
}
|
||||
|
||||
TowerPlaneRec* FGTower::FindPlane(string ID) {
|
||||
//cout << "FindPlane called for " << ID << "...\n";
|
||||
tower_plane_rec_list_iterator twrItr;
|
||||
// Do the approach list first
|
||||
for(twrItr = appList.begin(); twrItr != appList.end(); twrItr++) {
|
||||
//cout << "appList callsign is " << (*twrItr)->plane.callsign << '\n';
|
||||
if((*twrItr)->plane.callsign == ID) return(*twrItr);
|
||||
}
|
||||
// Then the circuit list
|
||||
for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) {
|
||||
//cout << "circuitList callsign is " << (*twrItr)->plane.callsign << '\n';
|
||||
if((*twrItr)->plane.callsign == ID) return(*twrItr);
|
||||
}
|
||||
// And finally the hold list
|
||||
// Then the runway list
|
||||
//cout << "rwyList.size() is " << rwyList.size() << '\n';
|
||||
for(twrItr = rwyList.begin(); twrItr != rwyList.end(); twrItr++) {
|
||||
//cout << "rwyList callsign is " << (*twrItr)->plane.callsign << '\n';
|
||||
if((*twrItr)->plane.callsign == ID) return(*twrItr);
|
||||
}
|
||||
// The hold list
|
||||
for(twrItr = holdList.begin(); twrItr != holdList.end(); twrItr++) {
|
||||
if((*twrItr)->plane.callsign == ID) return(*twrItr);
|
||||
}
|
||||
// And finally the vacated list
|
||||
for(twrItr = vacatedList.begin(); twrItr != vacatedList.end(); twrItr++) {
|
||||
//cout << "vacatedList callsign is " << (*twrItr)->plane.callsign << '\n';
|
||||
if((*twrItr)->plane.callsign == ID) return(*twrItr);
|
||||
}
|
||||
SG_LOG(SG_ATC, SG_WARN, "Unable to find " << ID << " in FGTower::FindPlane(...)");
|
||||
//exit(-1);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
void FGTower::RemovePlane(string ID) {
|
||||
//cout << ident << " RemovePlane called for " << ID << '\n';
|
||||
// We have to be careful here - we want to erase the plane from all lists it is in,
|
||||
// but we can only delete it once, AT THE END.
|
||||
TowerPlaneRec* t = NULL;
|
||||
tower_plane_rec_list_iterator twrItr;
|
||||
for(twrItr = appList.begin(); twrItr != appList.end(); twrItr++) {
|
||||
if((*twrItr)->plane.callsign == ID) {
|
||||
t = *twrItr;
|
||||
twrItr = appList.erase(twrItr);
|
||||
appListItr = appList.begin();
|
||||
}
|
||||
}
|
||||
for(twrItr = depList.begin(); twrItr != depList.end(); twrItr++) {
|
||||
if((*twrItr)->plane.callsign == ID) {
|
||||
t = *twrItr;
|
||||
twrItr = depList.erase(twrItr);
|
||||
depListItr = depList.begin();
|
||||
}
|
||||
}
|
||||
for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) {
|
||||
if((*twrItr)->plane.callsign == ID) {
|
||||
t = *twrItr;
|
||||
twrItr = circuitList.erase(twrItr);
|
||||
circuitListItr = circuitList.begin();
|
||||
}
|
||||
}
|
||||
for(twrItr = holdList.begin(); twrItr != holdList.end(); twrItr++) {
|
||||
if((*twrItr)->plane.callsign == ID) {
|
||||
t = *twrItr;
|
||||
twrItr = holdList.erase(twrItr);
|
||||
holdListItr = holdList.begin();
|
||||
}
|
||||
}
|
||||
for(twrItr = rwyList.begin(); twrItr != rwyList.end(); twrItr++) {
|
||||
if((*twrItr)->plane.callsign == ID) {
|
||||
t = *twrItr;
|
||||
twrItr = rwyList.erase(twrItr);
|
||||
rwyListItr = rwyList.begin();
|
||||
}
|
||||
}
|
||||
for(twrItr = vacatedList.begin(); twrItr != vacatedList.end(); twrItr++) {
|
||||
if((*twrItr)->plane.callsign == ID) {
|
||||
t = *twrItr;
|
||||
twrItr = vacatedList.erase(twrItr);
|
||||
vacatedListItr = vacatedList.begin();
|
||||
}
|
||||
}
|
||||
for(twrItr = trafficList.begin(); twrItr != trafficList.end(); twrItr++) {
|
||||
if((*twrItr)->plane.callsign == ID) {
|
||||
t = *twrItr;
|
||||
twrItr = trafficList.erase(twrItr);
|
||||
trafficListItr = trafficList.begin();
|
||||
}
|
||||
}
|
||||
// And finally, delete the record if we found it.
|
||||
if(t) delete t;
|
||||
}
|
||||
|
||||
void FGTower::ReportDownwind(string ID) {
|
||||
//cout << "ReportDownwind(...) called\n";
|
||||
if(ID == "USER") {
|
||||
|
@ -1628,6 +2031,16 @@ void FGTower::ReportDownwind(string ID) {
|
|||
if(t) {
|
||||
t->downwindReported = true;
|
||||
responseReqd = true;
|
||||
// If the plane is in the app list, remove it and put it in the circuit list instead.
|
||||
// Ideally we might want to do this at the 2 mile report prior to 45 deg entry, but at
|
||||
// the moment that would b&gg?r up the constraint position calculations.
|
||||
RemoveFromAppList(ID);
|
||||
t->leg = DOWNWIND;
|
||||
t->pos = t->planePtr->GetPos();
|
||||
CalcETA(t);
|
||||
//cout << "DDDDDDDDDDDDDDDDdddddddd" << endl;
|
||||
AddToCircuitList(t);
|
||||
//cout << "EEEEEEEEEEEEEEEEEEEEeeeeee" << endl;
|
||||
} else {
|
||||
SG_LOG(SG_ATC, SG_WARN, "WARNING: Unable to find plane " << ID << " in FGTower::ReportDownwind(...)");
|
||||
}
|
||||
|
@ -1730,7 +2143,7 @@ string FGTower::GenText(const string& m, int c) {
|
|||
else if ( strcmp ( tag, "@MI" ) == 0 ) {
|
||||
char buf[10];
|
||||
//sprintf( buf, "%3.1f", tpars.miles );
|
||||
int dist_miles = dclGetHorizontalSeparation(Point3D(lon, lat, elev), Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue())) / 1600;
|
||||
int dist_miles = (int)dclGetHorizontalSeparation(Point3D(lon, lat, elev), Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue())) / 1600;
|
||||
sprintf(buf, "%i", dist_miles);
|
||||
strcat( &dum[0], &buf[0] );
|
||||
}
|
||||
|
|
|
@ -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 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