1
0
Fork 0

Further progress towards interactive ATC

This commit is contained in:
daveluff 2003-11-05 17:24:58 +00:00
parent 6162d4462b
commit 430a44a803
5 changed files with 149 additions and 40 deletions

View file

@ -19,6 +19,17 @@
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
/*==========================================================
TODO list.
Should get pattern direction from tower.
Need to continually monitor and adjust deviation from glideslope
during descent to avoid occasionally landing short or long.
============================================================*/
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <config.h> # include <config.h>
#endif #endif

View file

@ -95,6 +95,7 @@ struct RunwayDetails {
double length; // In *METERS* double length; // In *METERS*
double width; // ditto double width; // ditto
string rwyID; string rwyID;
int patternDirection; // -1 for left, 1 for right
}; };
ostream& operator << (ostream& os, atc_type atc); ostream& operator << (ostream& os, atc_type atc);

View file

@ -254,41 +254,33 @@ void FGATCDialog::add_entry(string station, string transmission, string menutext
a.menuentry = menutext; a.menuentry = menutext;
a.callback_code = code; a.callback_code = code;
//atcmentrylist_station[station.c_str()].push_back(a);
(available_dialog[type])[station.c_str()].push_back(a); (available_dialog[type])[station.c_str()].push_back(a);
} }
void FGATCDialog::remove_entry( const string &station, const string &trans, atc_type type ) { void FGATCDialog::remove_entry( const string &station, const string &trans, atc_type type ) {
atcmentry_vec_type atcmlist = (available_dialog[type])[station]; atcmentry_vec_type* p = &((available_dialog[type])[station]);
atcmentry_vec_iterator current = atcmlist.begin(); atcmentry_vec_iterator current = p->begin();
atcmentry_vec_iterator last = atcmlist.end(); while(current != p->end()) {
if(current->transmission == trans) current = p->erase(current);
while(current != last) {
if(current->transmission == trans) current = atcmlist.erase(current);
else ++current; else ++current;
} }
} }
void FGATCDialog::remove_entry( const string &station, int code, atc_type type ) { void FGATCDialog::remove_entry( const string &station, int code, atc_type type ) {
atcmentry_vec_type atcmlist = (available_dialog[type])[station]; atcmentry_vec_type* p = &((available_dialog[type])[station]);
atcmentry_vec_iterator current = atcmlist.begin(); atcmentry_vec_iterator current = p->begin();
atcmentry_vec_iterator last = atcmlist.end(); while(current != p->end()) {
if(current->callback_code == code) current = p->erase(current);
while(current != last) {
if(current->callback_code == code) current = atcmlist.erase(current);
else ++current; else ++current;
} }
} }
// query the database whether the transmission is already registered; // query the database whether the transmission is already registered;
bool FGATCDialog::trans_reg( const string &station, const string &trans, atc_type type ) { bool FGATCDialog::trans_reg( const string &station, const string &trans, atc_type type ) {
//atcmentry_list_type atcmlist = atcmentrylist_station[station]; atcmentry_vec_type* p = &((available_dialog[type])[station]);
atcmentry_vec_type atcmlist = (available_dialog[type])[station]; atcmentry_vec_iterator current = p->begin();
atcmentry_vec_iterator current = atcmlist.begin(); for ( ; current != p->end() ; ++current ) {
atcmentry_vec_iterator last = atcmlist.end();
for ( ; current != last ; ++current ) {
if ( current->transmission == trans ) return true; if ( current->transmission == trans ) return true;
} }
return false; return false;
@ -296,12 +288,9 @@ bool FGATCDialog::trans_reg( const string &station, const string &trans, atc_typ
// query the database whether the transmission is already registered; // query the database whether the transmission is already registered;
bool FGATCDialog::trans_reg( const string &station, int code, atc_type type ) { bool FGATCDialog::trans_reg( const string &station, int code, atc_type type ) {
//atcmentry_list_type atcmlist = atcmentrylist_station[station]; atcmentry_vec_type* p = &((available_dialog[type])[station]);
atcmentry_vec_type atcmlist = (available_dialog[type])[station]; atcmentry_vec_iterator current = p->begin();
atcmentry_vec_iterator current = atcmlist.begin(); for ( ; current != p->end() ; ++current ) {
atcmentry_vec_iterator last = atcmlist.end();
for ( ; current != last ; ++current ) {
if ( current->callback_code == code ) return true; if ( current->callback_code == code ) return true;
} }
return false; return false;
@ -366,7 +355,7 @@ void FGATCDialog::PopupDialog() {
} }
optList[k] = NULL; optList[k] = NULL;
atcDialogCommunicationOptions->newList(optList); atcDialogCommunicationOptions->newList(optList);
atcDialogCommunicationOptions->setSize(w-100, h-100); atcDialogCommunicationOptions->setSize(w-100, h-90);
atcDialogCommunicationOptions->reveal(); atcDialogCommunicationOptions->reveal();
atcDialogMessage -> setLabel( "ATC Menu" ); atcDialogMessage -> setLabel( "ATC Menu" );
atcDialogMessage -> setPosition(w / 2, h - 30); atcDialogMessage -> setPosition(w / 2, h - 30);

View file

@ -45,6 +45,8 @@ TowerPlaneRec::TowerPlaneRec() :
longFinalAcknowledged(false), longFinalAcknowledged(false),
finalReported(false), finalReported(false),
finalAcknowledged(false), finalAcknowledged(false),
rwyVacatedReported(false),
rwyVacatedAcknowledged(false),
instructedToGoAround(false), instructedToGoAround(false),
onRwy(false), onRwy(false),
nextOnRwy(false), nextOnRwy(false),
@ -68,6 +70,8 @@ TowerPlaneRec::TowerPlaneRec(PlaneRec p) :
longFinalAcknowledged(false), longFinalAcknowledged(false),
finalReported(false), finalReported(false),
finalAcknowledged(false), finalAcknowledged(false),
rwyVacatedReported(false),
rwyVacatedAcknowledged(false),
instructedToGoAround(false), instructedToGoAround(false),
onRwy(false), onRwy(false),
nextOnRwy(false), nextOnRwy(false),
@ -91,6 +95,8 @@ TowerPlaneRec::TowerPlaneRec(Point3D pt) :
longFinalAcknowledged(false), longFinalAcknowledged(false),
finalReported(false), finalReported(false),
finalAcknowledged(false), finalAcknowledged(false),
rwyVacatedReported(false),
rwyVacatedAcknowledged(false),
instructedToGoAround(false), instructedToGoAround(false),
onRwy(false), onRwy(false),
nextOnRwy(false), nextOnRwy(false),
@ -115,6 +121,8 @@ TowerPlaneRec::TowerPlaneRec(PlaneRec p, Point3D pt) :
longFinalAcknowledged(false), longFinalAcknowledged(false),
finalReported(false), finalReported(false),
finalAcknowledged(false), finalAcknowledged(false),
rwyVacatedReported(false),
rwyVacatedAcknowledged(false),
instructedToGoAround(false), instructedToGoAround(false),
onRwy(false), onRwy(false),
nextOnRwy(false), nextOnRwy(false),
@ -388,6 +396,13 @@ void FGTower::ReceiveUserCallback(int code) {
VFRArrivalContact("USER", FULL_STOP); VFRArrivalContact("USER", FULL_STOP);
} else if(code == (int)USER_REQUEST_VFR_ARRIVAL_TOUCH_AND_GO) { } else if(code == (int)USER_REQUEST_VFR_ARRIVAL_TOUCH_AND_GO) {
VFRArrivalContact("USER", TOUCH_AND_GO); VFRArrivalContact("USER", TOUCH_AND_GO);
} else if(code == (int)USER_REPORT_DOWNWIND) {
ReportDownwind("USER");
} else if(code == (int)USER_REPORT_3_MILE_FINAL) {
// For now we'll just call report final instead of report long final to avoid having to alter the response code
ReportFinal("USER");
} else if(code == (int)USER_REPORT_RWY_VACATED) {
ReportRunwayVacated("USER");
} }
} }
@ -401,7 +416,23 @@ void FGTower::Respond() {
string trns = t->plane.callsign; string trns = t->plane.callsign;
trns += " "; trns += " ";
trns += name; trns += name;
trns += " tower Report three mile straight in for runway "; trns += " Tower";
// Should we clear staight in or for downwind entry?
// For now we'll clear straight in if greater than 1km from a line drawn through the threshold perpendicular to the rwy.
// Later on we might check the actual heading and direct some of those to enter on downwind or base.
Point3D op = ortho.ConvertToLocal(t->pos);
if(op.y() < -1000) {
trns += " Report three mile straight-in runway ";
current_atcdialog->add_entry(ident, "@AP Tower @CS @MI mile final Runway @RW", "Report Final", TOWER, (int)USER_REPORT_3_MILE_FINAL);
} 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 ");
trns += "downwind runway ";
current_atcdialog->add_entry(ident, "@AP Tower @CS Downwind @RW", "Report Downwind", TOWER, (int)USER_REPORT_DOWNWIND);
}
trns += ConvertRwyNumToSpokenString(activeRwy); trns += ConvertRwyNumToSpokenString(activeRwy);
if(display) { if(display) {
globals->get_ATC_display()->RegisterSingleMessage(trns, 0); globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
@ -415,16 +446,21 @@ void FGTower::Respond() {
for(tower_plane_rec_list_iterator twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) { for(tower_plane_rec_list_iterator twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) {
if((*twrItr)->plane.callsign == responseID) break; if((*twrItr)->plane.callsign == responseID) break;
++i; ++i;
} }
string trns = "Number "; string trns = t->plane.callsign;
trns += " Number ";
trns += ConvertNumToSpokenDigits(i); trns += ConvertNumToSpokenDigits(i);
trns += " "; trns += " ";
trns += t->plane.callsign; if(i == 1) {
if(display) { trns += "Cleared to land";
globals->get_ATC_display()->RegisterSingleMessage(trns, 0); t->clearedToLand = true;
} }
if(t->isUser && t->opType == TTT_UNKNOWN) { if(display) {
t->opType = CIRCUIT; globals->get_ATC_display()->RegisterSingleMessage(trns);
}
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);
} }
} else if(t->holdShortReported) { } else if(t->holdShortReported) {
if(t->nextOnRwy) { if(t->nextOnRwy) {
@ -469,6 +505,7 @@ void FGTower::Respond() {
} }
// TODO - add winds // TODO - add winds
t->clearedToLand = true; t->clearedToLand = true;
if(t->isUser) current_atcdialog->add_entry(ident, "@CS Clear of the runway", "Report runway vacated", TOWER, USER_REPORT_RWY_VACATED);
} else if(t->eta < 20) { } else if(t->eta < 20) {
// Do nothing - we'll be telling it to go around in less than 10 seconds if the // 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". // runway doesn't clear so no point in calling "continue approach".
@ -478,9 +515,27 @@ void FGTower::Respond() {
t->clearedToLand = false; t->clearedToLand = false;
} }
if(display && disp) { if(display && disp) {
globals->get_ATC_display()->RegisterSingleMessage(trns, 0); globals->get_ATC_display()->RegisterSingleMessage(trns);
} }
t->finalAcknowledged = true; t->finalAcknowledged = true;
} else if(t->rwyVacatedReported && !(t->rwyVacatedAcknowledged)) {
string trns = t->plane.callsign;
if(separateGround) {
trns += " Contact ground on ";
double f = globals->get_ATC_mgr()->GetFrequency(ident, GROUND) / 100.0;
char buf[10];
sprintf(buf, "%.2f", f);
trns += buf;
trns += " Good Day";
} else {
// Cop-out!!
trns += " cleared for taxi to the GA parking";
}
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 freqClear = true; // FIXME - set this to come true after enough time to render the message
@ -978,6 +1033,14 @@ void FGTower::DoRwyDetails() {
ortho.Init(rwy.threshold_pos, rwy.hdg); ortho.Init(rwy.threshold_pos, rwy.hdg);
rwy.end1ortho = ortho.ConvertToLocal(rwy.threshold_pos); // should come out as zero rwy.end1ortho = ortho.ConvertToLocal(rwy.threshold_pos); // should come out as zero
rwy.end2ortho = ortho.ConvertToLocal(takeoff_end); rwy.end2ortho = ortho.ConvertToLocal(takeoff_end);
// Set the pattern direction
// TODO - we'll check for a facilities file with this in eventually - for now assume left traffic except
// for certain circumstances (RH parallel rwy).
rwy.patternDirection = -1; // Left
if(rwy.rwyID.size() == 3) {
rwy.patternDirection = (rwy.rwyID.substr(2,1) == "R" ? 1 : -1);
}
} else { } else {
SG_LOG(SG_ATC, SG_ALERT, "Help - can't get good runway in FGTower!!"); SG_LOG(SG_ATC, SG_ALERT, "Help - can't get good runway in FGTower!!");
activeRwy = "NN"; activeRwy = "NN";
@ -1429,14 +1492,14 @@ void FGTower::VFRArrivalContact(string ID, LandingType opt) {
if(ID == "USER" || ID == usercall) { if(ID == "USER" || ID == usercall) {
t = FindPlane(usercall); t = FindPlane(usercall);
if(!t) { if(!t) {
cout << "NOT t\n"; //cout << "NOT t\n";
t = new TowerPlaneRec; t = new TowerPlaneRec;
t->isUser = true; t->isUser = true;
t->pos.setlon(user_lon_node->getDoubleValue()); t->pos.setlon(user_lon_node->getDoubleValue());
t->pos.setlat(user_lat_node->getDoubleValue()); t->pos.setlat(user_lat_node->getDoubleValue());
t->pos.setelev(user_elev_node->getDoubleValue()); t->pos.setelev(user_elev_node->getDoubleValue());
} else { } else {
cout << "IS t\n"; //cout << "IS t\n";
// Oops - the plane is already registered with this tower - maybe we took off and flew a giant circuit without // Oops - the plane is already registered with this tower - maybe we took off and flew a giant circuit without
// quite getting out of tower airspace - just ignore for now and treat as new arrival. // quite getting out of tower airspace - just ignore for now and treat as new arrival.
// TODO - Maybe should remove from departure and circuit list if in there though!! // TODO - Maybe should remove from departure and circuit list if in there though!!
@ -1463,6 +1526,10 @@ void FGTower::VFRArrivalContact(string ID, LandingType opt) {
appList.push_back(t); // Not necessarily permanent appList.push_back(t); // Not necessarily permanent
AddToTrafficList(t); AddToTrafficList(t);
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);
} }
void FGTower::RequestDepartureClearance(string ID) { void FGTower::RequestDepartureClearance(string ID) {
@ -1470,6 +1537,10 @@ void FGTower::RequestDepartureClearance(string ID) {
} }
void FGTower::ReportFinal(string ID) { void FGTower::ReportFinal(string ID) {
if(ID == "USER") {
ID = fgGetString("/sim/user/callsign");
current_atcdialog->remove_entry(ident, USER_REPORT_3_MILE_FINAL, TOWER);
}
TowerPlaneRec* t = FindPlane(ID); TowerPlaneRec* t = FindPlane(ID);
if(t) { if(t) {
t->finalReported = true; t->finalReported = true;
@ -1482,7 +1553,23 @@ void FGTower::ReportFinal(string ID) {
} }
} }
//void FGTower::ReportLongFinal(string ID); void FGTower::ReportLongFinal(string ID) {
if(ID == "USER") {
ID = fgGetString("/sim/user/callsign");
current_atcdialog->remove_entry(ident, USER_REPORT_3_MILE_FINAL, TOWER);
}
TowerPlaneRec* t = FindPlane(ID);
if(t) {
t->longFinalReported = true;
t->longFinalAcknowledged = false;
if(!(t->clearedToLand)) {
responseReqd = true;
} // possibly respond with wind even if already cleared to land?
} else {
SG_LOG(SG_ATC, SG_WARN, "WARNING: Unable to find plane " << ID << " in FGTower::ReportLongFinal(...)");
}
}
//void FGTower::ReportOuterMarker(string ID); //void FGTower::ReportOuterMarker(string ID);
//void FGTower::ReportMiddleMarker(string ID); //void FGTower::ReportMiddleMarker(string ID);
//void FGTower::ReportInnerMarker(string ID); //void FGTower::ReportInnerMarker(string ID);
@ -1490,6 +1577,17 @@ void FGTower::ReportFinal(string ID) {
void FGTower::ReportRunwayVacated(string ID) { void FGTower::ReportRunwayVacated(string ID) {
//cout << "Report Runway Vacated Called...\n"; //cout << "Report Runway Vacated Called...\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) {
t->rwyVacatedReported = true;
responseReqd = true;
} else {
SG_LOG(SG_ATC, SG_WARN, "WARNING: Unable to find plane " << ID << " in FGTower::ReportRunwayVacated(...)");
}
} }
TowerPlaneRec* FGTower::FindPlane(string ID) { TowerPlaneRec* FGTower::FindPlane(string ID) {
@ -1512,7 +1610,10 @@ TowerPlaneRec* FGTower::FindPlane(string ID) {
void FGTower::ReportDownwind(string ID) { void FGTower::ReportDownwind(string ID) {
//cout << "ReportDownwind(...) called\n"; //cout << "ReportDownwind(...) called\n";
// Tell the plane reporting what number she is in the circuit if(ID == "USER") {
ID = fgGetString("/sim/user/callsign");
current_atcdialog->remove_entry(ident, USER_REPORT_DOWNWIND, TOWER);
}
TowerPlaneRec* t = FindPlane(ID); TowerPlaneRec* t = FindPlane(ID);
if(t) { if(t) {
t->downwindReported = true; t->downwindReported = true;

View file

@ -57,7 +57,10 @@ enum tower_callback_type {
USER_REQUEST_VFR_DEPARTURE = 1, USER_REQUEST_VFR_DEPARTURE = 1,
USER_REQUEST_VFR_ARRIVAL = 2, USER_REQUEST_VFR_ARRIVAL = 2,
USER_REQUEST_VFR_ARRIVAL_FULL_STOP = 3, USER_REQUEST_VFR_ARRIVAL_FULL_STOP = 3,
USER_REQUEST_VFR_ARRIVAL_TOUCH_AND_GO = 4 USER_REQUEST_VFR_ARRIVAL_TOUCH_AND_GO = 4,
USER_REPORT_3_MILE_FINAL = 5,
USER_REPORT_DOWNWIND = 6,
USER_REPORT_RWY_VACATED = 7
}; };
// TODO - need some differentiation of IFR and VFR traffic in order to give the former priority. // TODO - need some differentiation of IFR and VFR traffic in order to give the former priority.
@ -89,6 +92,8 @@ public:
bool longFinalAcknowledged; bool longFinalAcknowledged;
bool finalReported; bool finalReported;
bool finalAcknowledged; bool finalAcknowledged;
bool rwyVacatedReported;
bool rwyVacatedAcknowledged;
bool instructedToGoAround; // set true if told by tower to go around bool instructedToGoAround; // set true if told by tower to go around
bool onRwy; // is physically on the runway bool onRwy; // is physically on the runway
bool nextOnRwy; // currently projected by tower to be the next on the runway bool nextOnRwy; // currently projected by tower to be the next on the runway
@ -153,6 +158,8 @@ public:
// in the future and consider multi-runway use, airplane weight etc. // in the future and consider multi-runway use, airplane weight etc.
inline string GetActiveRunway() { return activeRwy; } inline string GetActiveRunway() { return activeRwy; }
inline RunwayDetails GetActiveRunwayDetails() { return rwy; } inline RunwayDetails GetActiveRunwayDetails() { return rwy; }
// Get the pattern direction of the active rwy.
inline int GetPatternDirection() { return rwy.patternDirection; }
inline void SetDisplay() { display = true; } inline void SetDisplay() { display = true; }
inline void SetNoDisplay() { display = false; } inline void SetNoDisplay() { display = false; }