Further progress towards interactive ATC
This commit is contained in:
parent
6162d4462b
commit
430a44a803
5 changed files with 149 additions and 40 deletions
|
@ -19,6 +19,17 @@
|
|||
// along with this program; if not, write to the Free Software
|
||||
// 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
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
|
|
@ -95,6 +95,7 @@ struct RunwayDetails {
|
|||
double length; // In *METERS*
|
||||
double width; // ditto
|
||||
string rwyID;
|
||||
int patternDirection; // -1 for left, 1 for right
|
||||
};
|
||||
|
||||
ostream& operator << (ostream& os, atc_type atc);
|
||||
|
|
|
@ -254,41 +254,33 @@ void FGATCDialog::add_entry(string station, string transmission, string menutext
|
|||
a.menuentry = menutext;
|
||||
a.callback_code = code;
|
||||
|
||||
//atcmentrylist_station[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 ) {
|
||||
atcmentry_vec_type atcmlist = (available_dialog[type])[station];
|
||||
atcmentry_vec_iterator current = atcmlist.begin();
|
||||
atcmentry_vec_iterator last = atcmlist.end();
|
||||
|
||||
while(current != last) {
|
||||
if(current->transmission == trans) current = atcmlist.erase(current);
|
||||
atcmentry_vec_type* p = &((available_dialog[type])[station]);
|
||||
atcmentry_vec_iterator current = p->begin();
|
||||
while(current != p->end()) {
|
||||
if(current->transmission == trans) current = p->erase(current);
|
||||
else ++current;
|
||||
}
|
||||
}
|
||||
|
||||
void FGATCDialog::remove_entry( const string &station, int code, atc_type type ) {
|
||||
atcmentry_vec_type atcmlist = (available_dialog[type])[station];
|
||||
atcmentry_vec_iterator current = atcmlist.begin();
|
||||
atcmentry_vec_iterator last = atcmlist.end();
|
||||
|
||||
while(current != last) {
|
||||
if(current->callback_code == code) current = atcmlist.erase(current);
|
||||
atcmentry_vec_type* p = &((available_dialog[type])[station]);
|
||||
atcmentry_vec_iterator current = p->begin();
|
||||
while(current != p->end()) {
|
||||
if(current->callback_code == code) current = p->erase(current);
|
||||
else ++current;
|
||||
}
|
||||
}
|
||||
|
||||
// query the database whether the transmission is already registered;
|
||||
bool FGATCDialog::trans_reg( const string &station, const string &trans, atc_type type ) {
|
||||
//atcmentry_list_type atcmlist = atcmentrylist_station[station];
|
||||
atcmentry_vec_type atcmlist = (available_dialog[type])[station];
|
||||
atcmentry_vec_iterator current = atcmlist.begin();
|
||||
atcmentry_vec_iterator last = atcmlist.end();
|
||||
|
||||
for ( ; current != last ; ++current ) {
|
||||
atcmentry_vec_type* p = &((available_dialog[type])[station]);
|
||||
atcmentry_vec_iterator current = p->begin();
|
||||
for ( ; current != p->end() ; ++current ) {
|
||||
if ( current->transmission == trans ) return true;
|
||||
}
|
||||
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;
|
||||
bool FGATCDialog::trans_reg( const string &station, int code, atc_type type ) {
|
||||
//atcmentry_list_type atcmlist = atcmentrylist_station[station];
|
||||
atcmentry_vec_type atcmlist = (available_dialog[type])[station];
|
||||
atcmentry_vec_iterator current = atcmlist.begin();
|
||||
atcmentry_vec_iterator last = atcmlist.end();
|
||||
|
||||
for ( ; current != last ; ++current ) {
|
||||
atcmentry_vec_type* p = &((available_dialog[type])[station]);
|
||||
atcmentry_vec_iterator current = p->begin();
|
||||
for ( ; current != p->end() ; ++current ) {
|
||||
if ( current->callback_code == code ) return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -366,7 +355,7 @@ void FGATCDialog::PopupDialog() {
|
|||
}
|
||||
optList[k] = NULL;
|
||||
atcDialogCommunicationOptions->newList(optList);
|
||||
atcDialogCommunicationOptions->setSize(w-100, h-100);
|
||||
atcDialogCommunicationOptions->setSize(w-100, h-90);
|
||||
atcDialogCommunicationOptions->reveal();
|
||||
atcDialogMessage -> setLabel( "ATC Menu" );
|
||||
atcDialogMessage -> setPosition(w / 2, h - 30);
|
||||
|
|
|
@ -45,6 +45,8 @@ TowerPlaneRec::TowerPlaneRec() :
|
|||
longFinalAcknowledged(false),
|
||||
finalReported(false),
|
||||
finalAcknowledged(false),
|
||||
rwyVacatedReported(false),
|
||||
rwyVacatedAcknowledged(false),
|
||||
instructedToGoAround(false),
|
||||
onRwy(false),
|
||||
nextOnRwy(false),
|
||||
|
@ -68,6 +70,8 @@ TowerPlaneRec::TowerPlaneRec(PlaneRec p) :
|
|||
longFinalAcknowledged(false),
|
||||
finalReported(false),
|
||||
finalAcknowledged(false),
|
||||
rwyVacatedReported(false),
|
||||
rwyVacatedAcknowledged(false),
|
||||
instructedToGoAround(false),
|
||||
onRwy(false),
|
||||
nextOnRwy(false),
|
||||
|
@ -91,6 +95,8 @@ TowerPlaneRec::TowerPlaneRec(Point3D pt) :
|
|||
longFinalAcknowledged(false),
|
||||
finalReported(false),
|
||||
finalAcknowledged(false),
|
||||
rwyVacatedReported(false),
|
||||
rwyVacatedAcknowledged(false),
|
||||
instructedToGoAround(false),
|
||||
onRwy(false),
|
||||
nextOnRwy(false),
|
||||
|
@ -115,6 +121,8 @@ TowerPlaneRec::TowerPlaneRec(PlaneRec p, Point3D pt) :
|
|||
longFinalAcknowledged(false),
|
||||
finalReported(false),
|
||||
finalAcknowledged(false),
|
||||
rwyVacatedReported(false),
|
||||
rwyVacatedAcknowledged(false),
|
||||
instructedToGoAround(false),
|
||||
onRwy(false),
|
||||
nextOnRwy(false),
|
||||
|
@ -388,6 +396,13 @@ void FGTower::ReceiveUserCallback(int code) {
|
|||
VFRArrivalContact("USER", FULL_STOP);
|
||||
} else if(code == (int)USER_REQUEST_VFR_ARRIVAL_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;
|
||||
trns += " ";
|
||||
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);
|
||||
if(display) {
|
||||
globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
|
||||
|
@ -416,15 +447,20 @@ void FGTower::Respond() {
|
|||
if((*twrItr)->plane.callsign == responseID) break;
|
||||
++i;
|
||||
}
|
||||
string trns = "Number ";
|
||||
string trns = t->plane.callsign;
|
||||
trns += " Number ";
|
||||
trns += ConvertNumToSpokenDigits(i);
|
||||
trns += " ";
|
||||
trns += t->plane.callsign;
|
||||
if(display) {
|
||||
globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
|
||||
if(i == 1) {
|
||||
trns += "Cleared to land";
|
||||
t->clearedToLand = true;
|
||||
}
|
||||
if(t->isUser && t->opType == TTT_UNKNOWN) {
|
||||
t->opType = CIRCUIT;
|
||||
if(display) {
|
||||
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) {
|
||||
if(t->nextOnRwy) {
|
||||
|
@ -469,6 +505,7 @@ 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);
|
||||
} 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".
|
||||
|
@ -478,9 +515,27 @@ void FGTower::Respond() {
|
|||
t->clearedToLand = false;
|
||||
}
|
||||
if(display && disp) {
|
||||
globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
|
||||
globals->get_ATC_display()->RegisterSingleMessage(trns);
|
||||
}
|
||||
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
|
||||
|
@ -978,6 +1033,14 @@ void FGTower::DoRwyDetails() {
|
|||
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);
|
||||
|
||||
// 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 {
|
||||
SG_LOG(SG_ATC, SG_ALERT, "Help - can't get good runway in FGTower!!");
|
||||
activeRwy = "NN";
|
||||
|
@ -1429,14 +1492,14 @@ void FGTower::VFRArrivalContact(string ID, LandingType opt) {
|
|||
if(ID == "USER" || ID == usercall) {
|
||||
t = FindPlane(usercall);
|
||||
if(!t) {
|
||||
cout << "NOT t\n";
|
||||
//cout << "NOT t\n";
|
||||
t = new TowerPlaneRec;
|
||||
t->isUser = true;
|
||||
t->pos.setlon(user_lon_node->getDoubleValue());
|
||||
t->pos.setlat(user_lat_node->getDoubleValue());
|
||||
t->pos.setelev(user_elev_node->getDoubleValue());
|
||||
} 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
|
||||
// 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!!
|
||||
|
@ -1463,6 +1526,10 @@ void FGTower::VFRArrivalContact(string ID, LandingType opt) {
|
|||
|
||||
appList.push_back(t); // Not necessarily permanent
|
||||
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) {
|
||||
|
@ -1470,6 +1537,10 @@ void FGTower::RequestDepartureClearance(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);
|
||||
if(t) {
|
||||
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::ReportMiddleMarker(string ID);
|
||||
//void FGTower::ReportInnerMarker(string ID);
|
||||
|
@ -1490,6 +1577,17 @@ void FGTower::ReportFinal(string ID) {
|
|||
|
||||
void FGTower::ReportRunwayVacated(string ID) {
|
||||
//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) {
|
||||
|
@ -1512,7 +1610,10 @@ TowerPlaneRec* FGTower::FindPlane(string ID) {
|
|||
|
||||
void FGTower::ReportDownwind(string ID) {
|
||||
//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);
|
||||
if(t) {
|
||||
t->downwindReported = true;
|
||||
|
|
|
@ -57,7 +57,10 @@ 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_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.
|
||||
|
@ -89,6 +92,8 @@ public:
|
|||
bool longFinalAcknowledged;
|
||||
bool finalReported;
|
||||
bool finalAcknowledged;
|
||||
bool rwyVacatedReported;
|
||||
bool rwyVacatedAcknowledged;
|
||||
bool instructedToGoAround; // set true if 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
|
||||
|
@ -153,6 +158,8 @@ public:
|
|||
// in the future and consider multi-runway use, airplane weight etc.
|
||||
inline string GetActiveRunway() { return activeRwy; }
|
||||
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 SetNoDisplay() { display = false; }
|
||||
|
|
Loading…
Add table
Reference in a new issue