1
0
Fork 0

Remove legacy interactive tower and ground control

Also includes some whitespace cleanup of the remaining legacy ATC code.
This commit is contained in:
Dave Luff 2010-12-27 19:24:03 +00:00
parent 4c288398cb
commit a3433fbc6c
7 changed files with 75 additions and 4045 deletions

View file

@ -31,7 +31,6 @@
#include "commlist.hxx"
#include "ATCDialog.hxx"
#include "ATCutils.hxx"
#include "ground.hxx"
/*
@ -44,13 +43,7 @@ static void fgATCSearch( void ) {
AirportATC::AirportATC() :
atis_freq(0.0),
atis_active(false),
tower_freq(0.0),
tower_active(false),
ground_freq(0.0),
ground_active(false),
set_by_AI(false),
numAI(0)
atis_active(false)
//airport_atc_map.clear();
{
}
@ -123,17 +116,18 @@ void FGATCMgr::update(double dt) {
}
//cout << "Updating " << (*atc_list_itr)->get_ident() << ' ' << (*atc_list_itr)->GetType() << '\n';
//cout << "Freq = " << (*atc_list_itr)->get_freq() << '\n';
//cout << "Updating...\n";
(*atc_list_itr).second->Update(dt * atc_list->size());
//cout << "Done ATC update..." << endl;
++atc_list_itr;
}
#ifdef ATC_TEST
cout << "ATC_LIST: " << atc_list->size() << ' ';
//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';
//cout << '\n';
#endif
// Search the tuned frequencies every now and then - this should be done with the event scheduler
@ -169,17 +163,9 @@ bool FGATCMgr::CommRegisterAirport(const string& ident, int chan, const atc_type
SG_LOG(SG_ATC, SG_BULK, "Comm channel " << chan << " registered airport " << ident);
//cout << "Comm channel " << chan << " registered airport " << ident << ' ' << tp << '\n';
if(airport_atc_map.find(ident) != airport_atc_map.end()) {
//cout << "IN MAP - flagging set by comm..." << endl;
//xx airport_atc_map[ident]->set_by_comm[chan][tp] = true;
if(tp == ATIS || tp == AWOS) {
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;
@ -191,23 +177,9 @@ bool FGATCMgr::CommRegisterAirport(const string& ident, int chan, const atc_type
a->atis_freq = GetFrequency(ident, ATIS)
|| GetFrequency(ident, AWOS);
a->atis_active = false;
a->tower_freq = GetFrequency(ident, TOWER);
a->tower_active = false;
a->ground_freq = GetFrequency(ident, GROUND);
a->ground_active = false;
if(tp == ATIS || tp == AWOS) {
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;
//xx a->set_by_comm[chan][tp] = true;
}
airport_atc_map[ident] = a;
return(true);
}
@ -225,26 +197,25 @@ void FGATCMgr::ZapOtherService(const string ncunit, const string svc_name){
// OK, we have found some OTHER service;
// see if it is (was) active on our unit:
if (!actv.count(ncunit)) continue;
// cout << "Eradicating '" << svc->first << "' from: " << ncunit << endl;
//cout << "Eradicating '" << svc->first << "' from: " << ncunit << endl;
actv.erase(ncunit);
if (!actv.size()) {
//cout << "Eradicating service: '" << svc->first << "'" << endl;
svc->second->SetNoDisplay();
svc->second->Update(0); // one last update
SG_LOG(SG_GENERAL, SG_INFO, "would have erased ATC service:" << svc->second->get_name()<< "/"
<< svc->second->get_ident());
// delete svc->second;
atc_list->erase(svc);
// ALL pointers into the ATC list are now invalid,
// so let's reset them:
atc_list_itr = atc_list->begin();
svc->second->SetNoDisplay();
svc->second->Update(0); // one last update
SG_LOG(SG_GENERAL, SG_INFO, "would have erased ATC service:" << svc->second->get_name()<< "/"
<< svc->second->get_ident());
// delete svc->second;
atc_list->erase(svc);
// ALL pointers into the ATC list are now invalid,
// so let's reset them:
atc_list_itr = atc_list->begin();
}
break; // cannot be duplicates in the active list
}
}
}
// 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 ***
@ -264,81 +235,6 @@ bool FGATCMgr::GetAirportATCDetails(const string& icao, AirportATC* a) {
}
}
// Return a pointer to a given sort of ATC at a given airport and activate if necessary
// Returns NULL if service doesn't exist - calling function should check for this.
// We really ought to make this private and call it from the CommRegisterAirport / AIRegisterAirport functions
// - at the moment all these GetATC... functions exposed are just too complicated.
FGATC* FGATCMgr::GetATCPointer(const string& icao, const 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 << "In GetATCPointer, found " << icao << ' ' << type << endl;
AirportATC *a = airport_atc_map[icao];
//cout << "a->lon = " << a->lon << '\n';
//cout << "a->elev = " << a->elev << '\n';
//cout << "a->tower_freq = " << a->tower_freq << '\n';
switch(type) {
case TOWER:
if(a->tower_active) {
// Get the pointer from the list
return(FindInList(icao, type));
} else {
ATCData data;
if(current_commlist->FindByFreq(a->geod, a->tower_freq, &data, TOWER)) {
FGTower* t = new FGTower;
t->SetData(&data);
(*atc_list)[icao+decimalNumeral(type)] = t;
a->tower_active = true;
airport_atc_map[icao] = a;
//cout << "Initing tower " << icao << " in GetATCPointer()\n";
t->Init();
return(t);
} else {
SG_LOG(SG_ATC, SG_ALERT, "ERROR - tower that should exist in FGATCMgr::GetATCPointer for airport " << icao << " not found");
}
}
break;
case APPROACH:
break;
case ATIS: case AWOS:
SG_LOG(SG_ATC, SG_ALERT, "ERROR - ATIS station should not be requested from FGATCMgr::GetATCPointer");
break;
case GROUND:
//cout << "IN CASE GROUND" << endl;
if(a->ground_active) {
// Get the pointer from the list
return(FindInList(icao, type));
} else {
ATCData data;
if(current_commlist->FindByFreq(a->geod, a->ground_freq, &data, GROUND)) {
FGGround* g = new FGGround;
g->SetData(&data);
(*atc_list)[icao+decimalNumeral(type)] = g;
a->ground_active = true;
airport_atc_map[icao] = a;
g->Init();
return(g);
} else {
SG_LOG(SG_ATC, SG_ALERT, "ERROR - ground control that should exist in FGATCMgr::GetATCPointer for airport " << icao << " not found");
}
}
break;
case INVALID:
break;
case ENROUTE:
break;
case DEPARTURE:
break;
}
SG_LOG(SG_ATC, SG_ALERT, "ERROR IN FGATCMgr - reached end of GetATCPointer");
//cout << "ERROR IN FGATCMgr - reached end of GetATCPointer" << endl;
return(NULL);
}
// Return a pointer to an appropriate voice for a given type of ATC
// creating the voice if necessary - ie. make sure exactly one copy
// of every voice in use exists in memory.
@ -398,10 +294,10 @@ FGATCVoice* FGATCMgr::GetVoicePointer(const atc_type& type) {
void FGATCMgr::FreqSearch(const string navcomm, const int unit) {
string ncunit = navcomm + "[" + decimalNumeral(unit) + "]";
string ncunit = navcomm + "[" + decimalNumeral(unit) + "]";
string commbase = "/instrumentation/" + ncunit;
string commfreq = commbase + "/frequencies/selected-mhz";
SGPropertyNode_ptr comm_node = fgGetNode(commfreq.c_str(), false);
SGPropertyNode_ptr comm_node = fgGetNode(commfreq.c_str(), false);
//cout << "FreqSearch: " << ncunit
// << " node: " << comm_node << endl;
@ -410,48 +306,47 @@ void FGATCMgr::FreqSearch(const string navcomm, const int unit) {
ATCData data;
double freq = comm_node->getDoubleValue();
_aircraftPos = SGGeod::fromDegFt(lon_node->getDoubleValue(),
lat_node->getDoubleValue(), elev_node->getDoubleValue());
lat_node->getDoubleValue(), elev_node->getDoubleValue());
// Query the data store and get the closest match if any
// Query the data store and get the closest match if any
//cout << "Will FindByFreq: " << lat << " " << lon << " " << elev
// << " freq: " << freq << endl;
if(current_commlist->FindByFreq(_aircraftPos, freq, &data)) {
//cout << "FoundByFreq: " << freq
// << " ident: " << data.ident
// << " type: " << data.type << " ***" << endl;
// We are in range of something.
//cout << "FoundByFreq: " << freq
// << " ident: " << data.ident
// << " type: " << data.type << " ***" << endl;
// We are in range of something.
// Get rid of any *other* service that was on this radio unit:
string svc_name = data.ident+decimalNumeral(data.type);
// Get rid of any *other* service that was on this radio unit:
string svc_name = data.ident+decimalNumeral(data.type);
ZapOtherService(ncunit, svc_name);
// See if the service already exists, possibly connected to
// some other radio unit:
// See if the service already exists, possibly connected to
// some other radio unit:
if (atc_list->count(svc_name)) {
// make sure the service knows it's tuned on this radio:
FGATC* svc = (*atc_list)[svc_name];
svc->active_on[ncunit] = 1;
svc->SetDisplay();
if (data.type == APPROACH) svc->AddPlane("Player");
return;
// make sure the service knows it's tuned on this radio:
FGATC* svc = (*atc_list)[svc_name];
svc->active_on[ncunit] = 1;
svc->SetDisplay();
return;
}
CommRegisterAirport(data.ident, unit, data.type);
// This was a switch-case statement but the compiler didn't like
// the new variable creation with it.
if (data.type == ATIS
|| data.type == AWOS) (*atc_list)[svc_name] = new FGATIS;
else if (data.type == TOWER) (*atc_list)[svc_name] = new FGTower;
else if (data.type == GROUND) (*atc_list)[svc_name] = new FGGround;
FGATC* svc = (*atc_list)[svc_name];
svc->SetData(&data);
svc->active_on[ncunit] = 1;
svc->SetDisplay();
svc->Init();
if (data.type == APPROACH) svc->AddPlane("Player");
// This was a switch-case statement but the compiler didn't like
// the new variable creation with it.
if(data.type == ATIS || data.type == AWOS) {
(*atc_list)[svc_name] = new FGATIS;
FGATC* svc = (*atc_list)[svc_name];
if(svc != NULL) {
svc->SetData(&data);
svc->active_on[ncunit] = 1;
svc->SetDisplay();
svc->Init();
}
}
} else {
// No services in range. Zap any service on this unit.
ZapOtherService(ncunit, "x x x");
// No services in range. Zap any service on this unit.
ZapOtherService(ncunit, "x x x");
}
}

View file

@ -31,7 +31,7 @@
#include <list>
#include <map>
#include "tower.hxx"
#include "ATC.hxx"
using std::string;
using std::list;
@ -47,22 +47,6 @@ struct AirportATC {
SGGeod geod;
float atis_freq;
bool atis_active;
float tower_freq;
bool tower_active;
float ground_freq;
bool ground_active;
//float approach_freq;
//bool approach_active;
//float departure_freq;
//bool departure_active;
// NOTE - the *_active flags determine whether the service is active in atc_list,
// *NOT* whether the tower etc is closed or not!!!!
// Flags to ensure the stations don't get wrongly deactivated
bool set_by_AI; // true when the AI manager has activated this station
unsigned int numAI; // Ref count of the number of AI planes registered
//xx bool set_by_comm[2][ATC_NUM_TYPES]; // true when the relevant comm_freq has activated this station and type
};
class FGATCMgr : public SGSubsystem
@ -70,7 +54,7 @@ class FGATCMgr : public SGSubsystem
private:
bool initDone; // Hack - guard against update getting called before init
bool initDone; // Hack - guard against update getting called before init
// A map of airport ID vs frequencies and ATC provision
typedef map < string, AirportATC* > airport_atc_map_type;
@ -105,14 +89,12 @@ private:
bool last_in_range;
//FGATIS atis;
//FGGround ground;
FGTower tower;
// Voice related stuff
bool voice; // Flag - true if we are using voice
// Voice related stuff
bool voice; // Flag - true if we are using voice
#ifdef ENABLE_AUDIO_SUPPORT
bool voiceOK; // Flag - true if at least one voice has loaded OK
FGATCVoice* v1;
bool voiceOK; // Flag - true if at least one voice has loaded OK
FGATCVoice* v1;
#endif
public:
@ -130,31 +112,27 @@ public:
// Returns true if the airport is found in the map
bool GetAirportATCDetails(const string& icao, AirportATC* a);
// Return a pointer to a given sort of ATC at a given airport and activate if necessary
// Returns NULL if service doesn't exist - calling function should check for this.
FGATC* GetATCPointer(const string& icao, const atc_type& type);
// Return a pointer to an appropriate voice for a given type of ATC
// creating the voice if necessary - ie. make sure exactly one copy
// of every voice in use exists in memory.
//
// TODO - in the future this will get more complex and dole out country/airport
// specific voices, and possible make sure that the same voice doesn't get used
// at different airports in quick succession if a large enough selection are available.
FGATCVoice* GetVoicePointer(const atc_type& type);
atc_type GetComm1ATCType() { return(INVALID/* kludge */); }
FGATC* GetComm1ATCPointer() { return(0/* kludge */); }
atc_type GetComm2ATCType() { return(INVALID); }
FGATC* GetComm2ATCPointer() { return(0/* kludge */); }
// Get the frequency of a given service at a given airport
// Returns zero if not found
unsigned short int GetFrequency(const string& ident, const atc_type& tp);
// Register the fact that the comm radio is tuned to an airport
bool CommRegisterAirport(const string& ident, int chan, const atc_type& tp);
// Return a pointer to an appropriate voice for a given type of ATC
// creating the voice if necessary - ie. make sure exactly one copy
// of every voice in use exists in memory.
//
// TODO - in the future this will get more complex and dole out country/airport
// specific voices, and possible make sure that the same voice doesn't get used
// at different airports in quick succession if a large enough selection are available.
FGATCVoice* GetVoicePointer(const atc_type& type);
atc_type GetComm1ATCType() { return(INVALID/* kludge */); }
FGATC* GetComm1ATCPointer() { return(0/* kludge */); }
atc_type GetComm2ATCType() { return(INVALID); }
FGATC* GetComm2ATCPointer() { return(0/* kludge */); }
// Get the frequency of a given service at a given airport
// Returns zero if not found
unsigned short int GetFrequency(const string& ident, const atc_type& tp);
// Register the fact that the comm radio is tuned to an airport
bool CommRegisterAirport(const string& ident, int chan, const atc_type& tp);
private:
@ -163,9 +141,8 @@ private:
void ZapOtherService(const string ncunit, const string svc_name);
// 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 ***
// Return NULL if the given service is not in the list
// - *** THE CALLING FUNCTION MUST CHECK FOR THIS ***
FGATC* FindInList(const string& id, const atc_type& tp);
// Search the specified radio for stations on the same frequency and in range.

View file

@ -3,8 +3,6 @@ noinst_LIBRARIES = libATCDCL.a
libATCDCL_a_SOURCES = \
ATC.hxx ATC.cxx \
atis.hxx atis.cxx \
tower.hxx tower.cxx \
ground.hxx ground.cxx \
commlist.hxx commlist.cxx \
ATCDialog.hxx ATCDialog.cxx \
ATCVoice.hxx ATCVoice.cxx \

View file

@ -1,600 +0,0 @@
// FGGround - a class to provide ground control at larger airports.
//
// Written by David Luff, started March 2002.
//
// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <iostream>
#include <simgear/misc/sg_path.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/constants.h>
#include <Main/globals.hxx>
#include <stdlib.h>
#include <fstream>
#include "ground.hxx"
#include "ATCutils.hxx"
#include "ATCmgr.hxx"
using std::ifstream;
using std::cout;
node::node() {
}
node::~node() {
for(unsigned int i=0; i < arcs.size(); ++i) {
delete arcs[i];
}
}
// Make sure that a_path.cost += distance is safe from the moment it's created.
a_path::a_path() {
cost = 0;
}
FGGround::FGGround() {
ATCmgr = globals->get_ATC_mgr();
_type = GROUND;
networkLoadOK = false;
ground_traffic.erase(ground_traffic.begin(), ground_traffic.end());
ground_traffic_itr = ground_traffic.begin();
// 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);
// TODO - get the actual airport elevation
aptElev = 0.0;
}
FGGround::FGGround(const string& id) {
ATCmgr = globals->get_ATC_mgr();
networkLoadOK = false;
ground_traffic.erase(ground_traffic.begin(), ground_traffic.end());
ground_traffic_itr = ground_traffic.begin();
ident = id;
// 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);
// TODO - get the actual airport elevation
aptElev = 0.0;
}
FGGround::~FGGround() {
}
void FGGround::ParseRwyExits(node* np, char* es) {
char* token;
char estr[20];
strcpy(estr, es);
const char delimiters[] = "-";
token = strtok(estr, delimiters);
while(token != NULL) {
int i = atoi(token);
//cout << "token = " << token << endl;
//cout << "rwy number = " << i << endl;
//runways[(atoi(token))].exits.push_back(np);
runways[i].exits.push_back(np);
//cout << "token = " << token << '\n';
token = strtok(NULL, delimiters);
}
}
// Load the ground logical network of the current instances airport
// Return true if successfull.
// TODO - currently the file is assumed to reside in the base/ATC directory.
// This might change to something more thought out in the future.
// NOTE - currently it is assumed that all nodes are loaded before any arcs.
// It won't work ATM if this doesn't hold true.
bool FGGround::LoadNetwork() {
node* np;
arc* ap;
Gate* gp;
int gateCount = 0; // This is used to allocate gateID's from zero upwards
// This may well change in the future - probably to reading in the real-world
// gate numbers from file.
ifstream fin;
SGPath path = globals->get_fg_root();
//string taxiPath = "ATC/" + ident + ".taxi";
string taxiPath = "ATC/KEMT.taxi"; // FIXME - HARDWIRED FOR TESTING
path.append(taxiPath);
SG_LOG(SG_ATC, SG_INFO, "Trying to read taxiway data for " << ident << "...");
//cout << "Trying to read taxiway data for " << ident << "..." << endl;
fin.open(path.c_str(), ios::in);
if(!fin) {
SG_LOG(SG_ATC, SG_ALERT, "Unable to open taxiway data input file " << path.c_str());
//cout << "Unable to open taxiway data input file " << path.c_str() << endl;
return(false);
}
char ch;
char buf[30];
while(!fin.eof()) {
fin >> buf;
// Node, arc, or [End]?
//cout << "Read in ground network element type = " << buf << endl;
if(!strcmp(buf, "[End]")) { // TODO - maybe make this more robust to spelling errors by just looking for '['
SG_LOG(SG_ATC, SG_INFO, "Done reading " << path.c_str() << endl);
break;
} else if(!strcmp(buf, "N")) {
// Node
np = new node;
np->struct_type = NODE;
fin >> buf;
np->nodeID = atoi(buf);
fin >> buf;
np->pos.setLongitudeDeg(atof(buf));
fin >> buf;
np->pos.setLatitudeDeg(atof(buf));
fin >> buf;
np->pos.setElevationM(atof(buf));
fin >> buf; // node type
if(!strcmp(buf, "J")) {
np->type = JUNCTION;
} else if(!strcmp(buf, "T")) {
np->type = TJUNCTION;
} else if(!strcmp(buf, "H")) {
np->type = HOLD;
} else {
SG_LOG(SG_ATC, SG_ALERT, "**** ERROR ***** Unknown node type in taxi network...\n");
delete np;
return(false);
}
fin >> buf; // rwy exit information - gets parsed later - FRAGILE - will break if buf is reused.
// Now the name
fin >> ch; // strip the leading " off
np->name = "";
while(1) {
fin.unsetf(ios::skipws);
fin >> ch;
if((ch == '"') || (ch == 0x0A)) {
break;
} // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the "
np->name += ch;
}
fin.setf(ios::skipws);
network.push_back(np);
// FIXME - fragile - replies on buf not getting modified from exits read to here
// see if we also need to push it onto the runway exit list
//cout << "strlen(buf) = " << strlen(buf) << endl;
if(strlen(buf) > 2) {
//cout << "Calling ParseRwyExits for " << buf << endl;
ParseRwyExits(np, buf);
}
} else if(!strcmp(buf, "A")) {
ap = new arc;
ap->struct_type = ARC;
fin >> buf;
ap->n1 = atoi(buf);
fin >> buf;
ap->n2 = atoi(buf);
fin >> buf;
if(!strcmp(buf, "R")) {
ap->type = RUNWAY;
} else if(!strcmp(buf, "T")) {
ap->type = TAXIWAY;
} else {
SG_LOG(SG_ATC, SG_ALERT, "**** ERROR ***** Unknown arc type in taxi network...\n");
delete ap;
return(false);
}
// directed?
fin >> buf;
if(!strcmp(buf, "Y")) {
ap->directed = true;
} else if(!strcmp(buf, "N")) {
ap->directed = false;
} else {
SG_LOG(SG_ATC, SG_ALERT, "**** ERROR ***** Unknown arc directed value in taxi network - should be Y/N !!!\n");
delete ap;
return(false);
}
// Now the name
ap->name = "";
while(1) {
fin.unsetf(ios::skipws);
fin >> ch;
ap->name += ch;
if((ch == '"') || (ch == 0x0A)) {
break;
} // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the "
}
fin.setf(ios::skipws);
ap->distance = (int)dclGetHorizontalSeparation(network[ap->n1]->pos, network[ap->n2]->pos);
//cout << "Distance = " << ap->distance << '\n';
network[ap->n1]->arcs.push_back(ap);
network[ap->n2]->arcs.push_back(ap);
} else if(!strcmp(buf, "G")) {
gp = new Gate;
gp->struct_type = NODE;
gp->type = GATE;
fin >> buf;
gp->nodeID = atoi(buf);
fin >> buf;
gp->pos.setLongitudeDeg(atof(buf));
fin >> buf;
gp->pos.setLatitudeDeg(atof(buf));
fin >> buf;
gp->pos.setElevationM(atof(buf));
fin >> buf; // gate type - ignore this for now
fin >> buf; // gate heading
gp->heading = atoi(buf);
// Now the name
gp->name = "";
while(1) {
fin.unsetf(ios::skipws);
fin >> ch;
gp->name += ch;
if((ch == '"') || (ch == 0x0A)) {
break;
} // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the "
}
fin.setf(ios::skipws);
gp->id = gateCount; // Warning - this will likely change in the future.
gp->used = false;
network.push_back(gp);
gates[gateCount] = gp;
gateCount++;
} else {
// Something has gone seriously pear-shaped
SG_LOG(SG_ATC, SG_ALERT, "********* ERROR - unknown ground network element type... aborting read of " << path.c_str() << '\n');
return(false);
}
fin >> skipeol;
}
return(true);
}
void FGGround::Init() {
untowered = false;
// Figure out which is the active runway - TODO - it would be better to have ground call tower
// for runway operation details, but at the moment we can't guarantee that tower control at a given airport
// will be initialised before ground so we can't do that.
DoRwyDetails();
//cout << "In FGGround::Init, active rwy is " << activeRwy << '\n';
ortho.Init(rwy.threshold_pos, rwy.hdg);
networkLoadOK = LoadNetwork();
}
void FGGround::Update(double dt) {
// Call the base class update for the response time handling.
FGATC::Update(dt);
}
// Figure out which runways are active.
// For now we'll just be simple and do one active runway - eventually this will get much more complex
// Copied from FGTower - TODO - it would be better to implement this just once, and have ground call tower
// for runway operation details, but at the moment we can't guarantee that tower control at a given airport
// will be initialised before ground so we can't do that.
void FGGround::DoRwyDetails() {
//cout << "GetRwyDetails called" << endl;
const FGAirport* apt = fgFindAirportID(ident);
if (!apt) {
SG_LOG(SG_ATC, SG_WARN, "FGGround::DoRwyDetails: unknown ICAO:" << ident);
return;
}
FGRunway* runway = apt->getActiveRunwayForUsage();
activeRwy = runway->ident();
rwy.rwyID = runway->ident();
SG_LOG(SG_ATC, SG_INFO, "In FGGround, active runway for airport " << ident << " is " << activeRwy);
// Get the threshold position
double other_way = runway->headingDeg() - 180.0;
while(other_way <= 0.0) {
other_way += 360.0;
}
// move to the +l end/center of the runway
//cout << "Runway center is at " << runway._lon << ", " << runway._lat << '\n';
double tshlon = 0.0, tshlat = 0.0, tshr = 0.0;
double tolon = 0.0, tolat = 0.0, tor = 0.0;
rwy.length = runway->lengthM();
geo_direct_wgs_84 ( aptElev, runway->latitude(), runway->longitude(), other_way,
rwy.length / 2.0 - 25.0, &tshlat, &tshlon, &tshr );
geo_direct_wgs_84 ( aptElev, runway->latitude(), runway->longitude(), runway->headingDeg(),
rwy.length / 2.0 - 25.0, &tolat, &tolon, &tor );
// Note - 25 meters in from the runway end is a bit of a hack to put the plane ahead of the user.
// now copy what we need out of runway into rwy
rwy.threshold_pos = SGGeod::fromDegM(tshlon, tshlat, aptElev);
SGGeod takeoff_end = SGGeod::fromDegM(tolon, tolat, aptElev);
//cout << "Threshold position = " << tshlon << ", " << tshlat << ", " << aptElev << '\n';
//cout << "Takeoff position = " << tolon << ", " << tolat << ", " << aptElev << '\n';
rwy.hdg = runway->headingDeg();
// Set the projection for the local area based on this active runway
ortho.Init(rwy.threshold_pos, rwy.hdg);
rwy.end1ortho = ortho.ConvertToLocal(rwy.threshold_pos); // should come out as zero
rwy.end2ortho = ortho.ConvertToLocal(takeoff_end);
}
// Return a random gate ID of an unused gate.
// Two error values may be returned and must be checked for by the calling function:
// -2 signifies that no gates exist at this airport.
// -1 signifies that all gates are currently full.
int FGGround::GetRandomGateID() {
// Check that this airport actually has some gates!!
if(!gates.size()) {
return(-2);
}
gate_vec_type gateVec;
int num = 0;
int thenum;
int ID;
gatesItr = gates.begin();
while(gatesItr != gates.end()) {
if((gatesItr->second)->used == false) {
gateVec.push_back(gatesItr->second);
num++;
}
++gatesItr;
}
// Check that there are some unused gates!
if(!gateVec.size()) {
return(-1);
}
// Randomly select one from the list
sg_srandom_time();
thenum = (int)(sg_random() * gateVec.size());
ID = gateVec[thenum]->id;
return(ID);
}
// Return a pointer to an unused gate node
Gate* FGGround::GetGateNode() {
int id = GetRandomGateID();
if(id < 0) {
return(NULL);
} else {
return(gates[id]);
}
}
node* FGGround::GetHoldShortNode(const 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.
node* FGGround::GetThresholdNode(const string& rwyID) {
// For now go through all the nodes and parse their names
// Maybe in the future we'll map threshold nodes by ID
//cout << "Size of network is " << network.size() << '\n';
for(unsigned int i=0; i<network.size(); ++i) {
//cout << "Name = " << network[i]->name << '\n';
if(network[i]->name.size()) {
string s = network[i]->name;
// Warning - the next bit is fragile and dependent on my current naming scheme
//cout << "substr = " << s.substr(0,3) << '\n';
//cout << "size of s = " << s.size() << '\n';
if(s.substr(0,3) == "rwy") {
//cout << "subsubstr = " << s.substr(4, s.size() - 4) << '\n';
if(s.substr(4, s.size() - 4) == rwyID) {
return network[i];
}
}
}
}
return NULL;
}
// Get a path from a point on a runway to a gate
// TODO !!
// Get a path from a node to another node
// Eventually we will need complex algorithms for this taking other traffic,
// shortest path and suitable paths into accout.
// For now we'll just call the shortest path algorithm.
ground_network_path_type FGGround::GetPath(node* A, node* B) {
return(GetShortestPath(A, B));
};
// Get a path from a node to a runway threshold
ground_network_path_type FGGround::GetPath(node* A, const 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 for airport " << ident << '\n');
ground_network_path_type emptyPath;
emptyPath.erase(emptyPath.begin(), emptyPath.end());
return(emptyPath);
}
return GetShortestPath(A, b);
}
// Get a path from a node to a runway hold short point
// Bit of a hack this at the moment!
ground_network_path_type FGGround::GetPathToHoldShort(node* A, const string& rwyID) {
ground_network_path_type path = GetPath(A, rwyID);
path.pop_back(); // That should be the threshold stripped of
path.pop_back(); // and that should be the arc from hold short to threshold
// This isn't robust though - TODO - implement properly!
return(path);
}
// A shortest path algorithm from memory (ie. I can't find the bl&*dy book again!)
// I'm sure there must be enchancements that we can make to this, such as biasing the
// order in which the nodes are searched out from in favour of those geographically
// closer to the destination.
// Note that we are working with the master set of nodes and arcs so we mustn't change
// or delete them - we only delete the paths that we create during the algorithm.
ground_network_path_type FGGround::GetShortestPath(node* A, node* B) {
a_path* pathPtr;
shortest_path_map_type pathMap;
node_array_type nodesLeft;
// Debugging check
int pathsCreated = 0;
// Initialise the algorithm
nodesLeft.push_back(A);
pathPtr = new a_path;
pathsCreated++;
pathPtr->path.push_back(A);
pathPtr->cost = 0;
pathMap[A->nodeID] = pathPtr;
bool solution_found = false; // Flag to indicate that at least one candidate path has been found
int solution_cost = -1; // Cost of current best cost solution. -1 indicates no solution found yet.
a_path solution_path;
node* nPtr; // nPtr is used to point to the node we are currently working with
while(nodesLeft.size()) {
//cout << "\n*****nodesLeft*****\n";
//for(unsigned int i=0; i<nodesLeft.size(); ++i) {
//cout << nodesLeft[i]->nodeID << '\n';
//}
//cout << "*******************\n\n";
nPtr = *nodesLeft.begin(); // Thought - definate optimization possibilities here in the choice of which nodes we process first.
nodesLeft.erase(nodesLeft.begin());
//cout << "Walking out from node " << nPtr->nodeID << '\n';
for(unsigned int i=0; i<nPtr->arcs.size(); ++i) {
//cout << "ARC TO " << ((nPtr->arcs[i]->n1 == nPtr->nodeID) ? nPtr->arcs[i]->n2 : nPtr->arcs[i]->n1) << '\n';
}
if((solution_found) && (solution_cost <= pathMap[nPtr->nodeID]->cost)) {
// Do nothing - we've already found a solution and this partial path is already more expensive
} else {
// This path could still be better than the current solution - check it out
for(unsigned int i=0; i<(nPtr->arcs.size()); i++) {
// Map the new path against the end node, ie. *not* the one we just started with.
unsigned int end_nodeID = ((nPtr->arcs[i]->n1 == nPtr->nodeID) ? nPtr->arcs[i]->n2 : nPtr->arcs[i]->n1);
//cout << "end_nodeID = " << end_nodeID << '\n';
//cout << "pathMap size is " << pathMap.size() << '\n';
if(end_nodeID == nPtr->nodeID) {
//cout << "Circular arc!\n";
// Then its a circular arc - don't bother!!
//nPtr->arcs.erase(nPtr->arcs.begin() + i);
} else {
// see if the end node is already in the map or not
if(pathMap.find(end_nodeID) == pathMap.end()) {
//cout << "Not in the map" << endl;;
// Not in the map - easy!
pathPtr = new a_path;
pathsCreated++;
*pathPtr = *pathMap[nPtr->nodeID]; // *copy* the path
pathPtr->path.push_back(nPtr->arcs[i]);
pathPtr->path.push_back(network[end_nodeID]);
pathPtr->cost += nPtr->arcs[i]->distance;
pathMap[end_nodeID] = pathPtr;
nodesLeft.push_back(network[end_nodeID]); // By definition this can't be in the list already, or
// it would also have been in the map and hence OR'd with this one.
if(end_nodeID == B->nodeID) {
//cout << "Solution found!!!" << endl;
// Since this node wasn't in the map this is by definition the first solution
solution_cost = pathPtr->cost;
solution_path = *pathPtr;
solution_found = true;
}
} else {
//cout << "Already in the map" << endl;
// In the map - not so easy - need to get rid of an arc from the higher cost one.
//cout << "Current cost of node " << end_nodeID << " is " << pathMap[end_nodeID]->cost << endl;
int newCost = pathMap[nPtr->nodeID]->cost + nPtr->arcs[i]->distance;
//cout << "New cost is of node " << nPtr->nodeID << " is " << newCost << endl;
if(newCost >= pathMap[end_nodeID]->cost) {
// No need to do anything.
//cout << "Not doing anything!" << endl;
} else {
delete pathMap[end_nodeID];
pathsCreated--;
pathPtr = new a_path;
pathsCreated++;
*pathPtr = *pathMap[nPtr->nodeID]; // *copy* the path
pathPtr->path.push_back(nPtr->arcs[i]);
pathPtr->path.push_back(network[end_nodeID]);
pathPtr->cost += nPtr->arcs[i]->distance;
pathMap[end_nodeID] = pathPtr;
// We need to add this node to the list-to-do again to force a recalculation
// onwards from this node with the new lower cost to node cost.
nodesLeft.push_back(network[end_nodeID]);
if(end_nodeID == B->nodeID) {
//cout << "Solution found!!!" << endl;
// Need to check if there is a previous better solution
if((solution_cost < 0) || (pathPtr->cost < solution_cost)) {
solution_cost = pathPtr->cost;
solution_path = *pathPtr;
solution_found = true;
}
}
}
}
}
}
}
}
// delete all the paths before returning
shortest_path_map_iterator spItr = pathMap.begin();
while(spItr != pathMap.end()) {
if(spItr->second != NULL) {
delete spItr->second;
--pathsCreated;
}
++spItr;
}
//cout << "pathsCreated = " << pathsCreated << '\n';
if(pathsCreated > 0) {
SG_LOG(SG_ATC, SG_ALERT, "WARNING - Possible memory leak in FGGround::GetShortestPath\n\
Please report to flightgear-devel@flightgear.org\n");
}
//cout << (solution_found ? "Result: solution found\n" : "Result: no solution found\n");
return(solution_path.path); // TODO - we really ought to have a fallback position incase a solution isn't found.
}
// Randomly or otherwise populate some of the gates with parked planes
// (might eventually be done by the AIMgr if and when lots of AI traffic is generated)
// Return 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
node_array_type FGGround::GetExits(const string& rwyID) {
// FIXME - get a 07L or similar in here and we're stuffed!!!
return(runways[atoi(rwyID.c_str())].exits);
}

View file

@ -1,328 +0,0 @@
// FGGround - a class to provide ground control at larger airports.
//
// Written by David Luff, started March 2002.
//
// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef _FG_GROUND_HXX
#define _FG_GROUND_HXX
#include <map>
#include <vector>
#include <list>
#include <simgear/math/SGMath.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/props/props.hxx>
#include "ATC.hxx"
#include "ATCProjection.hxx"
#include <string>
class FGATCMgr;
//////////////////////////////////////////////////////
// Types for the logical network data structure
enum arc_type {
RUNWAY,
TAXIWAY
};
enum node_type {
GATE,
APRON,
HOLD,
JUNCTION,
TJUNCTION
};
enum GateType {
TRANSPORT_PASSENGER,
TRANSPORT_PASSENGER_NARROWBODY,
TRANSPORT_PASSENGER_WIDEBODY,
TRANSPORT_CARGO,
GA_LOCAL,
GA_LOCAL_SINGLE,
GA_LOCAL_TWIN,
GA_TRANSIENT,
GA_TRANSIENT_SINGLE,
GA_TRANSIENT_TWIN,
OTHER // ie. anything goes!!
};
enum network_element_type {
NODE,
ARC
};
struct ground_network_element {
network_element_type struct_type;
};
struct arc : public ground_network_element {
int distance;
std::string name;
arc_type type;
bool directed; //false if 2-way, true if 1-way.
//This is a can of worms since arcs might be one way in different directions under different circumstances
unsigned int n1; // The nodeID of the first node
unsigned int n2; // The nodeID of the second node
// If the arc is directed then flow is normally from n1 to n2. See the above can of worms comment though.
};
typedef std::vector <arc*> arc_array_type; // This was and may become again a list instead of vector
typedef arc_array_type::iterator arc_array_iterator;
typedef arc_array_type::const_iterator arc_array_const_iterator;
struct node : public ground_network_element {
node();
~node();
unsigned int nodeID; //each node in an airport needs a unique ID number - this is ZERO-BASED to match array position
SGGeod pos;
SGVec3d orthoPos;
std::string name;
node_type type;
arc_array_type arcs;
double max_turn_radius;
};
typedef std::vector <node*> node_array_type;
typedef node_array_type::iterator node_array_iterator;
typedef node_array_type::const_iterator node_array_const_iterator;
struct Gate : public node {
GateType gateType;
int max_weight; //units??
//airline_code airline; //For the future - we don't have any airline codes ATM
int id; // The gate number in the logical scheme of things
std::string name; // The real-world gate letter/number
//node* pNode;
bool used;
double heading; // The direction the parked-up plane should point in degrees
};
typedef std::vector < Gate* > gate_vec_type;
typedef gate_vec_type::iterator gate_vec_iterator;
typedef gate_vec_type::const_iterator gate_vec_const_iterator;
// A map of gate vs. the logical (internal FGFS) gate ID
typedef std::map < int, Gate* > gate_map_type;
typedef gate_map_type::iterator gate_map_iterator;
typedef gate_map_type::const_iterator gate_map_const_iterator;
// Runways - all the runway stuff is likely to change in the future
struct Rwy {
int id; //note this is a very simplified scheme for now - R & L are not differentiated
//It should work for simple one rwy airports
node_array_type exits; //Array of available exits from runway
// should probably add an FGRunway structure here as well eventually
// Eventually we will also want some encoding of real-life preferred runways
// This will get us up and running for single runway airports though.
};
typedef std::vector < Rwy > runway_array_type;
typedef runway_array_type::iterator runway_array_iterator;
typedef runway_array_type::const_iterator runway_array_const_iterator;
// end logical network types
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// Structures to use the network
// A path through the network
typedef std::vector < ground_network_element* > ground_network_path_type;
typedef ground_network_path_type::iterator ground_network_path_iterator;
typedef ground_network_path_type::const_iterator ground_network_path_const_iterator;
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////
//
// Stuff for the shortest-path algorithms
struct a_path {
a_path();
ground_network_path_type path;
int cost;
};
// Paths mapped by nodeID reached so-far
typedef std::map < unsigned int, a_path* > shortest_path_map_type;
typedef shortest_path_map_type::iterator shortest_path_map_iterator;
// Nodes mapped by their ID
//typedef map < unsigned int, node* > node_map_type;
//typedef node_map_type::iterator node_map_iterator;
////////////////////////////////////////////////
// Planes active within the ground network.
// A more specialist plane rec to include ground information
struct GroundRec {
PlaneRec plane;
node* destination;
node* last_clearance;
bool taxiRequestOutstanding; // Plane has requested taxi and we haven't responded yet
double clearanceCounter; // Hack for communication timing - counter since clearance requested in seconds
bool cleared; // set true when the plane has been cleared to somewhere
bool incoming; //true for arrivals, false for departures
// status?
// Almost certainly need to add more here
};
typedef std::list < GroundRec* > ground_rec_list;
typedef ground_rec_list::iterator ground_rec_list_itr;
typedef ground_rec_list::const_iterator ground_rec_list_const_itr;
///////////////////////////////////////////////////////////////////////////////
//
// FGGround
//
///////////////////////////////////////////////////////////////////////////////
class FGGround : public FGATC {
public:
FGGround();
FGGround(const std::string& id);
~FGGround();
void Init();
void Update(double dt);
inline const std::string& get_trans_ident() { return trans_ident; }
// Randomly fill some of the available gates and GA parking spots with planes
void PopulateGates();
// Return a suitable gate (maybe this should be a list of suitable gates so the plane or controller can choose the closest one)
void ReturnGate(Gate &gate, GateType type);
// Return a pointer to an unused gate
Gate* GetGateNode();
// Return a pointer to a hold short node
node* GetHoldShortNode(const std::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
node_array_type GetExits(const std::string& rwyID);
// Get a path from one node to another
ground_network_path_type GetPath(node* A, node* B);
// Get a path from a node to a runway threshold
ground_network_path_type GetPath(node* A, const std::string& rwyID);
// Get a path from a node to a runway hold short point
// Bit of a hack this at the moment!
ground_network_path_type GetPathToHoldShort(node* A, const std::string& rwyID);
private:
FGATCMgr* ATCmgr;
// This is purely for synactic convienience to avoid writing globals->get_ATC_mgr()-> all through the code!
// Need a data structure to hold details of the various active planes
// Need a data structure to hold details of the logical network
// including which gates are filled - or possibly another data structure
// with the positions of the inactive planes.
// Need a data structure to hold outstanding communications from aircraft.
// Possibly need a data structure to hold outstanding communications to aircraft.
// The logical network
// NODES WILL BE STORED IN THE NETWORK IN ORDER OF nodeID NUMBER
// ie. NODE 5 WILL BE AT network[5]
node_array_type network;
// A map of all the gates indexed against internal (FGFS) ID
gate_map_type gates;
gate_map_iterator gatesItr;
FGATCAlignedProjection ortho;
// Planes currently active
//ground_rec_list ground_traffic;
// Find the shortest route through the logical network between two points.
//FindShortestRoute(point a, point b);
// Assign a gate or parking location to a new arrival
//AssignGate(ground_rec &g);
// Generate the next clearance for an airplane
//NextClearance(ground_rec &g);
// environment - need to make sure we're getting the surface winds and not winds aloft.
SGPropertyNode_ptr wind_from_hdg; //degrees
SGPropertyNode_ptr wind_speed_knots; //knots
// for failure modeling
std::string trans_ident; // transmitted ident
bool ground_failed; // ground failed?
bool networkLoadOK; // Indicates whether LoadNetwork returned true or false at last attempt
// Tower control
bool untowered; // True if this is an untowered airport (we still need the ground class for shortest path implementation etc
//FGATC* tower; // Pointer to the tower control
// Logical runway details - this might change in the future.
//runway_array_type runways; // STL way
Rwy runways[37]; // quick hack!
// Physical runway details
double aptElev; // Airport elevation
std::string activeRwy; // Active runway number - For now we'll disregard multiple / alternate runway operation.
RunwayDetails rwy; // Assumed to be the active one for now.// Figure out which runways are active.
// For now we'll just be simple and do one active runway - eventually this will get much more complex
// Copied from FGTower - TODO - it would be better to implement this just once, and have ground call tower
// for runway operation details, but at the moment we can't guarantee that tower control at a given airport
// will be initialised before ground so we can't do that.
void DoRwyDetails();
// Load the logical ground network for this airport from file.
// Return true if successfull.
bool LoadNetwork();
// Parse a runway exit string and push the supplied node pointer onto the runway exit list
void ParseRwyExits(node* np, char* es);
// Return a random gate ID of an unused gate.
// Two error values may be returned and must be checked for by the calling function:
// -2 signifies that no gates exist at this airport.
// -1 signifies that all gates are currently full.
// TODO - modify to return a suitable gate based on aircraft size/weight.
int GetRandomGateID();
// Return a pointer to the node at a runway threshold
// Returns NULL if unsuccessful.
node* GetThresholdNode(const std::string& rwyID);
// A shortest path algorithm from memory (I can't find the bl&*dy book again!)
ground_network_path_type GetShortestPath(node* A, node* B);
// Planes
ground_rec_list ground_traffic;
ground_rec_list_itr ground_traffic_itr;
};
#endif // _FG_GROUND_HXX

File diff suppressed because it is too large Load diff

View file

@ -1,374 +0,0 @@
// FGTower - a class to provide tower control at towered airports.
//
// Written by David Luff, started March 2002.
//
// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef _FG_TOWER_HXX
#define _FG_TOWER_HXX
#include <simgear/compiler.h>
#include <simgear/math/SGMath.hxx>
#include <simgear/misc/sgstream.hxx>
#include <iosfwd>
#include <string>
#include <list>
#include "ATC.hxx"
#include "ATCProjection.hxx"
class FGATCMgr;
class FGGround;
//DCL - a complete guess for now.
#define FG_TOWER_DEFAULT_RANGE 30
enum tower_traffic_type {
CIRCUIT,
INBOUND, // CIRCUIT traffic gets changed to INBOUND when on final of the full-stop circuit.
OUTBOUND,
TTT_UNKNOWN, // departure, but we don't know if for circuits or leaving properly
STRAIGHT_IN
};
ostream& operator << (ostream& os, tower_traffic_type ttt);
enum PatternLeg {
TAKEOFF_ROLL,
CLIMBOUT,
TURN1,
CROSSWIND,
TURN2,
DOWNWIND,
TURN3,
BASE,
TURN4,
FINAL,
LANDING_ROLL,
LEG_UNKNOWN
};
ostream& operator << (ostream& os, PatternLeg pl);
enum LandingType {
FULL_STOP,
STOP_AND_GO,
TOUCH_AND_GO,
AIP_LT_UNKNOWN
};
ostream& operator << (ostream& os, LandingType lt);
enum tower_callback_type {
USER_REQUEST_VFR_DEPARTURE = 1,
USER_REQUEST_VFR_ARRIVAL = 2,
USER_REQUEST_VFR_ARRIVAL_FULL_STOP = 3,
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_GOING_AROUND = 8,
USER_REQUEST_TAKE_OFF = 9
};
// TODO - need some differentiation of IFR and VFR traffic in order to give the former priority.
// Structure for holding details of a plane under tower control.
// Not fixed yet - may include more stuff later.
class TowerPlaneRec {
public:
TowerPlaneRec();
TowerPlaneRec(const PlaneRec& p);
TowerPlaneRec(const SGGeod& pt);
TowerPlaneRec(const PlaneRec& p, const SGGeod& pt);
PlaneRec plane;
SGGeod pos;
double eta; // seconds
double dist_out; // meters from theshold
bool clearedToLand;
bool clearedToLineUp;
bool clearedToTakeOff;
// ought to add time cleared to depart so we can nag if necessary
bool holdShortReported;
bool lineUpReported;
bool downwindReported;
bool longFinalReported;
bool longFinalAcknowledged;
bool finalReported;
bool finalAcknowledged;
bool rwyVacatedReported;
bool rwyVacatedAcknowledged;
bool goAroundReported; // set true if plane informs tower that it's going around.
bool instructedToGoAround; // set true if plane told by tower to go around.
bool onRwy; // is physically on the runway
bool nextOnRwy; // currently projected by tower to be the next on the runway
bool gearWasUp; // Tell to ATC about gear
bool gearUpReported; // Tell to pilot about landing gear
bool vfrArrivalReported;
bool vfrArrivalAcknowledged;
// Type of operation the plane is doing
tower_traffic_type opType;
// Whereabouts in circuit if doing circuits
PatternLeg leg;
LandingType landingType;
bool isUser; // true if this plane is the user
};
typedef std::list < TowerPlaneRec* > tower_plane_rec_list_type;
typedef tower_plane_rec_list_type::iterator tower_plane_rec_list_iterator;
typedef tower_plane_rec_list_type::const_iterator tower_plane_rec_list_const_iterator;
class FGTower : public FGATC {
public:
FGTower();
~FGTower();
void Init();
void Update(double dt);
void ReceiveUserCallback(int code);
// 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.
void VFRArrivalContact(const std::string& ID, const LandingType& opt = AIP_LT_UNKNOWN);
void RequestDepartureClearance(const std::string& ID);
void RequestTakeOffClearance(const std::string& ID);
void ReportFinal(const std::string& ID);
void ReportLongFinal(const std::string& ID);
void ReportOuterMarker(const std::string& ID);
void ReportMiddleMarker(const std::string& ID);
void ReportInnerMarker(const std::string& ID);
void ReportRunwayVacated(const std::string& ID);
void ReportReadyForDeparture(const std::string& ID);
void ReportDownwind(const std::string& ID);
void ReportGoingAround(const std::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 const std::string& GetActiveRunway() const { return activeRwy; }
inline const RunwayDetails& GetActiveRunwayDetails() const { return rwy; }
// Get the pattern direction of the active rwy.
inline int GetPatternDirection() const { return rwy.patternDirection; }
inline const std::string& get_trans_ident() const { return trans_ident; }
inline FGGround* const GetGroundPtr() const { return ground; }
// Returns true if positions of crosswind/downwind/base leg turns should be constrained by previous traffic
// plus the constraint position as a rwy orientated orthopos (meters)
bool GetCrosswindConstraint(double& cpos);
bool GetDownwindConstraint(double& dpos);
bool GetBaseConstraint(double& bpos);
std::string GenText(const std::string& m, int c);
std::string GetWeather();
std::string GetATISID();
private:
FGATCMgr* ATCmgr;
// This is purely for synactic convienience to avoid writing globals->get_ATC_mgr()-> all through the code!
// Respond to a transmission
void Respond();
void ProcessRunwayVacatedReport(TowerPlaneRec* t);
void ProcessDownwindReport(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.
void ClearHoldingPlane(TowerPlaneRec* t);
// Find a pointer to plane of callsign ID within the internal data structures
TowerPlaneRec* FindPlane(const std::string& ID);
// Remove and delete all instances of a plane with a given ID
void RemovePlane(const std::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(const SGGeod& pt);
// Figure out if a given position lies on a runway or not
bool OnAnyRunway(const SGGeod& pt, bool onGround);
// Calculate the eta of a plane to the threshold.
// For ground traffic this is the fastest they can get there.
// For air traffic this is the middle approximation.
void CalcETA(TowerPlaneRec* tpr, bool printout = false);
// Iterate through all the std::lists and call CalcETA for all the planes.
void doThresholdETACalc();
// Order the std::list of traffic as per expected threshold use and flag any conflicts
bool doThresholdUseOrder();
// Calculate the crow-flys distance of a plane to the threshold in meters
double CalcDistOutM(TowerPlaneRec* tpr);
// Calculate the crow-flys distance of a plane to the threshold in miles
double CalcDistOutMiles(TowerPlaneRec* tpr);
/*
void doCommunication();
*/
void IssueLandingClearance(TowerPlaneRec* tpr);
void IssueGoAround(TowerPlaneRec* tpr);
void IssueDepartureClearance(TowerPlaneRec* tpr);
unsigned int update_count; // Convienince counter for speading computational load over several updates
unsigned int update_count_max; // ditto.
double timeSinceLastDeparture; // Time in seconds since last departure from active rwy.
bool departed; // set true when the above needs incrementing with time, false when it doesn't.
// environment - need to make sure we're getting the surface winds and not winds aloft.
SGPropertyNode_ptr wind_from_hdg; //degrees
SGPropertyNode_ptr wind_speed_knots; //knots
double aptElev; // Airport elevation
std::string activeRwy; // Active runway number - For now we'll disregard multiple / alternate runway operation.
RunwayDetails rwy; // Assumed to be the active one for now.
bool rwyOccupied; // Active runway occupied flag. For now we'll disregard land-and-hold-short operations.
FGATCAlignedProjection ortho; // Orthogonal mapping of the local area with the active runway threshold at the origin
FGATCAlignedProjection ortho_temp; // Ortho for any runway (needed to get plane position in airport)
// Figure out which runways are active.
// For now we'll just be simple and do one active runway - eventually this will get much more complex
// This is a private function - public interface to the results of this is through GetActiveRunway
void DoRwyDetails();
// Need a data structure to hold details of the various active planes
// or possibly another data structure with the positions of the inactive planes.
// Need a data structure to hold outstanding communications from aircraft.
// Linked-list of planes on approach to active rwy ordered with nearest first (timewise).
// Includes planes that have landed but not yet vacated runway.
// Somewhat analagous to the paper strips used (used to be used?) in real life.
// Doesn't include planes in circuit until they turn onto base/final?
// TODO - may need to alter this for operation to more than one active rwy.
tower_plane_rec_list_type appList;
tower_plane_rec_list_iterator appListItr;
// What should we do with planes approaching the airport to join the circuit somewhere
// but not on straight-in though? - put them in here for now.
tower_plane_rec_list_type circuitAppList;
tower_plane_rec_list_iterator circuitAppListItr;
// List of departed planes (planes doing circuits go into circuitList not depList after departure)
tower_plane_rec_list_type depList;
tower_plane_rec_list_iterator depListItr;
// List of planes in the circuit (ordered by nearest to landing first)
tower_plane_rec_list_type circuitList;
tower_plane_rec_list_iterator circuitListItr;
// List of planes waiting to depart
tower_plane_rec_list_type holdList;
tower_plane_rec_list_iterator holdListItr;
// List of planes on rwy
tower_plane_rec_list_type rwyList;
tower_plane_rec_list_iterator rwyListItr;
// List of all planes due to use a given rwy arranged in projected order of rwy use
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(const std::string& id);
bool RemoveFromAppList(const std::string& id);
bool RemoveFromRwyList(const std::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.
double GetTrafficETA(unsigned int list_pos, bool printout = false);
// Add a tower plane rec with ETA to the traffic list in the correct position ETA-wise.
// Returns true if this could cause a threshold ETA conflict with other traffic, false otherwise.
bool AddToTrafficList(TowerPlaneRec* t, bool holding = false);
bool AddToCircuitList(TowerPlaneRec* t);
// Add to vacated list only if not already present
void AddToVacatedList(TowerPlaneRec* t);
void AddToHoldingList(TowerPlaneRec* t);
// Ground can be separate or handled by tower in real life.
// In the program we will always use a separate FGGround class, but we need to know
// whether it is supposed to be separate or not to give the correct instructions.
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
std::string trans_ident; // transmitted ident
bool tower_failed; // tower failed?
// Pointers to current users position and orientation
SGPropertyNode_ptr user_lon_node;
SGPropertyNode_ptr user_lat_node;
SGPropertyNode_ptr user_elev_node;
SGPropertyNode_ptr user_hdg_node;
// Details of the general traffic flow etc in the circuit
double crosswind_leg_pos; // Distance from threshold crosswind leg is being turned to in meters (actual operation - *not* ideal circuit)
double downwind_leg_pos; // Actual offset distance in meters from the runway that planes are flying the downwind leg
// Currently not sure whether the above should be always +ve or just take the natural orthopos sign (+ve for RH circuit, -ve for LH).
double base_leg_pos; // Actual offset distance from the threshold (-ve) that planes are turning to base leg.
double nominal_crosswind_leg_pos;
double nominal_downwind_leg_pos;
double nominal_base_leg_pos;
friend std::istream& operator>> ( std::istream&, FGTower& );
};
#endif //_FG_TOWER_HXX