From 6162d4462b09069eb96e372e532b799caa7f2a16 Mon Sep 17 00:00:00 2001 From: daveluff Date: Wed, 5 Nov 2003 10:06:57 +0000 Subject: [PATCH] Further progress towards interactive ATC control --- src/ATC/ATC.cxx | 10 +- src/ATC/ATC.hxx | 7 +- src/ATC/ATCDialog.cxx | 89 ++++++---- src/ATC/ATCDialog.hxx | 11 ++ src/ATC/tower.cxx | 373 ++++++++++++++++++++++++++++++++---------- src/ATC/tower.hxx | 17 +- 6 files changed, 387 insertions(+), 120 deletions(-) diff --git a/src/ATC/ATC.cxx b/src/ATC/ATC.cxx index 3bc1f0607..7adb85c4f 100644 --- a/src/ATC/ATC.cxx +++ b/src/ATC/ATC.cxx @@ -65,8 +65,9 @@ void FGATC::SetResponseReqd(string rid) { respond = false; // TODO - this ignores the fact that more than one plane could call this before response // Shouldn't happen with AI only, but user could confuse things?? responseID = rid; + runResponseCounter = true; responseCounter = 0.0; - responseTime = 2.5; // TODO - randomize this slightly. + responseTime = 1.8; // TODO - randomize this slightly. } void FGATC::NotifyTransmissionFinished(string rid) { @@ -75,7 +76,7 @@ void FGATC::NotifyTransmissionFinished(string rid) { if(responseReqd) { runResponseCounter = true; responseCounter = 0.0; - responseTime = 2.5; // TODO - randomize this slightly. + responseTime = 1.8; // TODO - randomize this slightly. } else { freqClear = true; } @@ -165,6 +166,11 @@ void FGATC::NoRender(string refname) { } } +// Generate the text of a message from its parameters and the current context. +string FGATC::GenText(const string& m, int c) { + return(""); +} + ostream& operator << (ostream& os, atc_type atc) { switch(atc) { case(INVALID): return(os << "INVALID"); diff --git a/src/ATC/ATC.hxx b/src/ATC/ATC.hxx index ca6689d36..c0ba92241 100644 --- a/src/ATC/ATC.hxx +++ b/src/ATC/ATC.hxx @@ -126,6 +126,9 @@ public: // Indicate that this instance should not output to the display virtual void SetNoDisplay(); + // Generate the text of a message from its parameters and the current context. + virtual string GenText(const string& m, int c); + // Returns true if OK to transmit on this frequency inline bool GetFreqClear() { return freqClear; } // Indicate that the frequency is in use @@ -242,15 +245,15 @@ operator >> ( istream& fin, ATCData& a ) a.name = ""; fin >> ch; - a.name += ch; + if(ch != '"') a.name += ch; while(1) { //in >> noskipws fin.unsetf(ios::skipws); fin >> ch; - a.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 " + a.name += ch; } fin.setf(ios::skipws); //cout << "Comm name = " << a.name << '\n'; diff --git a/src/ATC/ATCDialog.cxx b/src/ATC/ATCDialog.cxx index 6b55eb770..8d390f1b8 100644 --- a/src/ATC/ATCDialog.cxx +++ b/src/ATC/ATCDialog.cxx @@ -28,6 +28,7 @@ #include "ATCDialog.hxx" #include "ATC.hxx" #include "ATCmgr.hxx" +#include "ATCdisplay.hxx" #include "commlist.hxx" #include "ATCutils.hxx" #include @@ -67,12 +68,12 @@ static void atcUppercase(string &s) { } // ----------------------- Popup Dialog Statics------------------ -static puDialogBox* atcDialog; -static puFrame* atcDialogFrame; -static puText* atcDialogMessage; -static puOneShot* atcDialogOkButton; -static puOneShot* atcDialogCancelButton; -static puButtonBox* atcDialogCommunicationOptions; +static puDialogBox* atcDialog; +static puFrame* atcDialogFrame; +static puText* atcDialogMessage; +static puOneShot* atcDialogOkButton; +static puOneShot* atcDialogCancelButton; +static puButtonBox* atcDialogCommunicationOptions; // -------------------------------------------------------------- // ----------------------- Freq Dialog Statics------------------- @@ -93,33 +94,12 @@ static puText* atcFreqDisplayText[ATC_MAX_FREQ_DISPLAY]; // -------------------------------------------------------------- //////////////// Popup callbacks /////////////////// -static void ATCDialogOK(puObject *) -{ - switch(atcDialogCommunicationOptions->getValue()) { - case 0: - //cout << "Option 0 chosen\n"; - fgSetBool("/sim/atc/opt0",true); - break; - case 1: - //cout << "Option 1 chosen\n"; - fgSetBool("/sim/atc/opt1",true); - break; - case 2: - //cout << "Option 2 chosen\n"; - fgSetBool("/sim/atc/opt2",true); - break; - case 3: - //cout << "Option 2 chosen\n"; - fgSetBool("/sim/atc/opt3",true); - break; - default: - break; - } +static void ATCDialogOK(puObject*) { + current_atcdialog->PopupCallback(); FG_POP_PUI_DIALOG( atcDialog ); } -static void ATCDialogCancel(puObject *) -{ +static void ATCDialogCancel(puObject*) { FG_POP_PUI_DIALOG( atcDialog ); } ////////////////////////////////////////////////// @@ -143,6 +123,11 @@ static void FreqDisplayOK(puObject*) { FGATCDialog::FGATCDialog() { + _callbackPending = false; + _callbackTimer = 0.0; + _callbackWait = 0.0; + _callbackPtr = NULL; + _callbackCode = 0; } FGATCDialog::~FGATCDialog() { @@ -247,6 +232,18 @@ void FGATCDialog::Init() { FG_FINALIZE_PUI_DIALOG(atcDialog); } +void FGATCDialog::Update(double dt) { + if(_callbackPending) { + if(_callbackTimer > _callbackWait) { + _callbackPtr->ReceiveUserCallback(_callbackCode); + _callbackPtr->NotifyTransmissionFinished(fgGetString("/sim/user/callsign")); + _callbackPending = false; + } else { + _callbackTimer += dt; + } + } +} + // Add an entry void FGATCDialog::add_entry(string station, string transmission, string menutext, atc_type type, int code) { @@ -389,7 +386,39 @@ void FGATCDialog::PopupDialog() { FG_PUSH_PUI_DIALOG(atcDialog); } +void FGATCDialog::PopupCallback() { + FGATC* atcptr = globals->get_ATC_mgr()->GetComm1ATCPointer(); // FIXME - Hardwired to comm1 at the moment + if(atcptr) { + if(atcptr->GetType() == APPROACH) { + switch(atcDialogCommunicationOptions->getValue()) { + case 0: + fgSetBool("/sim/atc/opt0",true); + break; + case 1: + fgSetBool("/sim/atc/opt1",true); + break; + case 2: + fgSetBool("/sim/atc/opt2",true); + break; + case 3: + fgSetBool("/sim/atc/opt3",true); + break; + default: + break; + } + } else if(atcptr->GetType() == TOWER) { + ATCMenuEntry a = ((available_dialog[TOWER])[(string)atcptr->get_ident()])[atcDialogCommunicationOptions->getValue()]; + atcptr->SetFreqInUse(); + globals->get_ATC_display()->RegisterSingleMessage(atcptr->GenText(a.transmission, a.callback_code)); + _callbackPending = true; + _callbackTimer = 0.0; + _callbackWait = 5.0; + _callbackPtr = atcptr; + _callbackCode = a.callback_code; + } + } +} void FGATCDialog::FreqDialog() { diff --git a/src/ATC/ATCDialog.hxx b/src/ATC/ATCDialog.hxx index d5295c8a3..85a6721e6 100644 --- a/src/ATC/ATCDialog.hxx +++ b/src/ATC/ATCDialog.hxx @@ -60,16 +60,22 @@ public: void Init(); + void Update(double dt); + void PopupDialog(); + void PopupCallback(); + void add_entry( string station, string transmission, string menutext, atc_type type, int code); void remove_entry( const string &station, const string &trans, atc_type type ); void remove_entry( const string &station, int code, atc_type type ); + // query the database whether the transmission is already registered; bool trans_reg( const string &station, const string &trans, atc_type type ); + // query the database whether the transmission is already registered; bool trans_reg( const string &station, int code, atc_type type ); // Display a frequency search dialog for nearby stations @@ -86,6 +92,11 @@ private: int freq; bool reset; + bool _callbackPending; + double _callbackTimer; + double _callbackWait; + FGATC* _callbackPtr; + int _callbackCode; }; extern FGATCDialog *current_atcdialog; diff --git a/src/ATC/tower.cxx b/src/ATC/tower.cxx index a747f74f3..d7fb6c9ff 100644 --- a/src/ATC/tower.cxx +++ b/src/ATC/tower.cxx @@ -27,6 +27,7 @@ #include "ATCdisplay.hxx" #include "ATCmgr.hxx" #include "ATCutils.hxx" +#include "ATCDialog.hxx" #include "commlist.hxx" #include "AILocalTraffic.hxx" @@ -35,86 +36,94 @@ SG_USING_STD(cout); // TowerPlaneRec TowerPlaneRec::TowerPlaneRec() : -clearedToLand(false), -clearedToLineUp(false), -clearedToTakeOff(false), -holdShortReported(false), -downwindReported(false), -longFinalReported(false), -longFinalAcknowledged(false), -finalReported(false), -finalAcknowledged(false), -instructedToGoAround(false), -onRwy(false), -nextOnRwy(false), -opType(TTT_UNKNOWN), -leg(LEG_UNKNOWN), -landingType(AIP_LT_UNKNOWN), -isUser(false) + clearedToLand(false), + clearedToLineUp(false), + clearedToTakeOff(false), + holdShortReported(false), + downwindReported(false), + longFinalReported(false), + longFinalAcknowledged(false), + finalReported(false), + finalAcknowledged(false), + instructedToGoAround(false), + onRwy(false), + nextOnRwy(false), + vfrArrivalReported(false), + vfrArrivalAcknowledged(false), + opType(TTT_UNKNOWN), + leg(LEG_UNKNOWN), + landingType(AIP_LT_UNKNOWN), + isUser(false) { plane.callsign = "UNKNOWN"; } TowerPlaneRec::TowerPlaneRec(PlaneRec p) : -clearedToLand(false), -clearedToLineUp(false), -clearedToTakeOff(false), -holdShortReported(false), -downwindReported(false), -longFinalReported(false), -longFinalAcknowledged(false), -finalReported(false), -finalAcknowledged(false), -instructedToGoAround(false), -onRwy(false), -nextOnRwy(false), -opType(TTT_UNKNOWN), -leg(LEG_UNKNOWN), -landingType(AIP_LT_UNKNOWN), -isUser(false) + clearedToLand(false), + clearedToLineUp(false), + clearedToTakeOff(false), + holdShortReported(false), + downwindReported(false), + longFinalReported(false), + longFinalAcknowledged(false), + finalReported(false), + finalAcknowledged(false), + instructedToGoAround(false), + onRwy(false), + nextOnRwy(false), + vfrArrivalReported(false), + vfrArrivalAcknowledged(false), + opType(TTT_UNKNOWN), + leg(LEG_UNKNOWN), + landingType(AIP_LT_UNKNOWN), + isUser(false) { plane = p; } TowerPlaneRec::TowerPlaneRec(Point3D pt) : -clearedToLand(false), -clearedToLineUp(false), -clearedToTakeOff(false), -holdShortReported(false), -downwindReported(false), -longFinalReported(false), -longFinalAcknowledged(false), -finalReported(false), -finalAcknowledged(false), -instructedToGoAround(false), -onRwy(false), -nextOnRwy(false), -opType(TTT_UNKNOWN), -leg(LEG_UNKNOWN), -landingType(AIP_LT_UNKNOWN), -isUser(false) + clearedToLand(false), + clearedToLineUp(false), + clearedToTakeOff(false), + holdShortReported(false), + downwindReported(false), + longFinalReported(false), + longFinalAcknowledged(false), + finalReported(false), + finalAcknowledged(false), + instructedToGoAround(false), + onRwy(false), + nextOnRwy(false), + vfrArrivalReported(false), + vfrArrivalAcknowledged(false), + opType(TTT_UNKNOWN), + leg(LEG_UNKNOWN), + landingType(AIP_LT_UNKNOWN), + isUser(false) { plane.callsign = "UNKNOWN"; pos = pt; } TowerPlaneRec::TowerPlaneRec(PlaneRec p, Point3D pt) : -clearedToLand(false), -clearedToLineUp(false), -clearedToTakeOff(false), -holdShortReported(false), -downwindReported(false), -longFinalReported(false), -longFinalAcknowledged(false), -finalReported(false), -finalAcknowledged(false), -instructedToGoAround(false), -onRwy(false), -nextOnRwy(false), -opType(TTT_UNKNOWN), -leg(LEG_UNKNOWN), -landingType(AIP_LT_UNKNOWN), -isUser(false) + clearedToLand(false), + clearedToLineUp(false), + clearedToTakeOff(false), + holdShortReported(false), + downwindReported(false), + longFinalReported(false), + longFinalAcknowledged(false), + finalReported(false), + finalAcknowledged(false), + instructedToGoAround(false), + onRwy(false), + nextOnRwy(false), + vfrArrivalReported(false), + vfrArrivalAcknowledged(false), + opType(TTT_UNKNOWN), + leg(LEG_UNKNOWN), + landingType(AIP_LT_UNKNOWN), + isUser(false) { plane = p; pos = pt; @@ -128,8 +137,6 @@ isUser(false) Currently user is assumed to have taken off again when leaving the runway - check speed/elev for taxiing-in. -AI plane lands even when user on rwy - make it go-around instead. - Tell AI plane to contact ground when taxiing in. Use track instead of heading to determine what leg of the circuit the user is flying. @@ -242,8 +249,8 @@ void FGTower::Init() { if(rwyOccupied) { // Assume the user is started at the threshold ready to take-off TowerPlaneRec* t = new TowerPlaneRec; - t->plane.callsign = "Charlie Foxtrot Sierra"; // C-FGFS !!! - fixme - this is a bit hardwired - t->plane.type = GA_SINGLE; + t->plane.callsign = fgGetString("/sim/user/callsign"); + t->plane.type = GA_SINGLE; // FIXME - hardwired!! t->opType = TTT_UNKNOWN; // We don't know if the user wants to do circuits or a departure... t->landingType = AIP_LT_UNKNOWN; t->leg = TAKEOFF_ROLL; @@ -252,6 +259,10 @@ void FGTower::Init() { t->clearedToTakeOff = true; rwyList.push_back(t); departed = false; + } else { + // For now assume that this means the user is not at the airport and is in the air. + // TODO FIXME - this will break when user starts on apron, at hold short, etc. + current_atcdialog->add_entry(ident, "@AP Tower @CS @MI miles @CD of the airport for full stop with the ATIS", "Contact tower for VFR arrival (full stop)", TOWER, (int)USER_REQUEST_VFR_ARRIVAL_FULL_STOP); } } @@ -274,6 +285,7 @@ void FGTower::Update(double dt) { cout << update_count << "\ttL: " << trafficList.size() << " cL: " << circuitList.size() << " hL: " << holdList.size() << " aL: " << appList.size() << '\n'; } */ + //if(ident == "EGNX") cout << display << '\n'; if(departed != false) { timeSinceLastDeparture += dt; @@ -368,17 +380,36 @@ void FGTower::Update(double dt) { } void FGTower::ReceiveUserCallback(int code) { - if(code == (int)USER_REQUEST_DEPARTURE) { + if(code == (int)USER_REQUEST_VFR_DEPARTURE) { cout << "User requested departure\n"; + } else if(code == (int)USER_REQUEST_VFR_ARRIVAL) { + VFRArrivalContact("USER"); + } else if(code == (int)USER_REQUEST_VFR_ARRIVAL_FULL_STOP) { + VFRArrivalContact("USER", FULL_STOP); + } else if(code == (int)USER_REQUEST_VFR_ARRIVAL_TOUCH_AND_GO) { + VFRArrivalContact("USER", TOUCH_AND_GO); } } void FGTower::Respond() { - //cout << "Entering Respond..." << endl; + cout << "Entering Respond, responseID = " << responseID << endl; TowerPlaneRec* t = FindPlane(responseID); if(t) { // This will grow!!! - if(t->downwindReported) { + if(t->vfrArrivalReported && !t->vfrArrivalAcknowledged) { + // Testing - hardwire straight in for now + string trns = t->plane.callsign; + trns += " "; + trns += name; + trns += " tower Report three mile straight in for runway "; + trns += ConvertRwyNumToSpokenString(activeRwy); + if(display) { + globals->get_ATC_display()->RegisterSingleMessage(trns, 0); + } else { + cout << "Not displaying, trns was " << trns << '\n'; + } + t->vfrArrivalAcknowledged = true; + } else if(t->downwindReported) { t->downwindReported = false; int i = 1; for(tower_plane_rec_list_iterator twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) { @@ -1384,28 +1415,51 @@ void FGTower::RegisterAIPlane(PlaneRec plane, FGAIPlane* ai, tower_traffic_type doThresholdUseOrder(); } -void FGTower::RequestLandingClearance(string ID) { +// Contact tower for VFR approach +// eg "Cessna Charlie Foxtrot Golf Foxtrot Sierra eight miles South of the airport for full stop with Bravo" +// This function probably only called via user interaction - AI planes will have an overloaded function taking a planerec. +// opt defaults to AIP_LT_UNKNOWN +void FGTower::VFRArrivalContact(string ID, LandingType opt) { //cout << "Request Landing Clearance called...\n"; - // Assume this comes from the user - have another function taking a pointer to the AIplane for the AI traffic. - // For now we'll also assume that the user is a light plane and can get him/her to join the circuit if necessary. - - TowerPlaneRec* t = new TowerPlaneRec; - t->isUser = true; - t->clearedToLand = false; - t->pos.setlon(user_lon_node->getDoubleValue()); - t->pos.setlat(user_lat_node->getDoubleValue()); - t->pos.setelev(user_elev_node->getDoubleValue()); + // For now we'll assume that the user is a light plane and can get him/her to join the circuit if necessary. + + TowerPlaneRec* t; + string usercall = fgGetString("/sim/user/callsign"); + if(ID == "USER" || ID == usercall) { + t = FindPlane(usercall); + if(!t) { + 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"; + // 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!! + } + } else { + // Oops - something has gone wrong - put out a warning + cout << "WARNING - FGTower::VFRContact(string ID, LandingType lt) called with ID " << ID << " which does not appear to be the user.\n"; + return; + } + // TODO - // Calculate where the user is in relation to the active runway and it's circuit + // Calculate where the plane is in relation to the active runway and it's circuit // and set the op-type as appropriate. // HACK - to get up and running I'm going to assume that the user contacts tower on a staight-in final for now. t->opType = STRAIGHT_IN; t->plane.type = GA_SINGLE; // FIXME - Another assumption! - t->plane.callsign = ID; + t->plane.callsign = usercall; + + t->vfrArrivalReported = true; + responseReqd = true; appList.push_back(t); // Not necessarily permanent AddToTrafficList(t); @@ -1468,6 +1522,159 @@ void FGTower::ReportDownwind(string ID) { } } +string FGTower::GenText(const string& m, int c) { + const int cmax = 300; + //string message; + char tag[4]; + char crej = '@'; + char mes[cmax]; + char dum[cmax]; + //char buf[10]; + char *pos; + int len; + //FGTransmission t; + string usercall = fgGetString("/sim/user/callsign"); + + //transmission_list_type tmissions = transmissionlist_station[station]; + //transmission_list_iterator current = tmissions.begin(); + //transmission_list_iterator last = tmissions.end(); + + //for ( ; current != last ; ++current ) { + // if ( current->get_code().c1 == code.c1 && + // current->get_code().c2 == code.c2 && + // current->get_code().c3 == code.c3 ) { + + //if ( ttext ) message = current->get_transtext(); + //else message = current->get_menutext(); + strcpy( &mes[0], m.c_str() ); + + // Replace all the '@' parameters with the actual text. + int check = 0; // If mes gets overflowed the while loop can go infinite + while ( strchr(&mes[0], crej) != NULL ) { // ie. loop until no more occurances of crej ('@') found + pos = strchr( &mes[0], crej ); + bcopy(pos, &tag[0], 3); + tag[3] = '\0'; + int i; + len = 0; + for ( i=0; igetDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue())) / 1600; + sprintf(buf, "%i", dist_miles); + strcat( &dum[0], &buf[0] ); + } + else if ( strcmp ( tag, "@FR" ) == 0 ) { + /* + char buf[10]; + sprintf( buf, "%6.2f", tpars.freq ); + strcat( &dum[0], &buf[0] ); + */ + } + else if ( strcmp ( tag, "@RW" ) == 0 ) { + strcat(&dum[0], ConvertRwyNumToSpokenString(activeRwy).c_str()); + } else if(strcmp(tag, "@CD") == 0) { // @CD = compass direction + double h = GetHeadingFromTo(Point3D(lon, lat, elev), Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue())); + while(h < 0.0) h += 360.0; + while(h > 360.0) h -= 360.0; + if(h < 22.5 || h > 337.5) { + strcat(&dum[0], "North"); + } else if(h < 67.5) { + strcat(&dum[0], "North-East"); + } else if(h < 112.5) { + strcat(&dum[0], "East"); + } else if(h < 157.5) { + strcat(&dum[0], "South-East"); + } else if(h < 202.5) { + strcat(&dum[0], "South"); + } else if(h < 247.5) { + strcat(&dum[0], "South-West"); + } else if(h < 292.5) { + strcat(&dum[0], "West"); + } else { + strcat(&dum[0], "North-West"); + } + } else { + cout << "Tag " << tag << " not found" << endl; + break; + } + strcat( &dum[0], &mes[len+3] ); + strcpy( &mes[0], &dum[0] ); + + ++check; + if(check > 10) { + SG_LOG(SG_ATC, SG_WARN, "WARNING: Possibly endless loop terminated in FGTransmissionlist::gen_text(...)"); + break; + } + } + + //cout << mes << endl; + //break; + //} + //} + if ( mes != "" ) return mes; + else return "No transmission found"; +} + ostream& operator << (ostream& os, tower_traffic_type ttt) { switch(ttt) { case(CIRCUIT): return(os << "CIRCUIT"); diff --git a/src/ATC/tower.hxx b/src/ATC/tower.hxx index ace19d982..57341e913 100644 --- a/src/ATC/tower.hxx +++ b/src/ATC/tower.hxx @@ -54,8 +54,10 @@ enum tower_traffic_type { ostream& operator << (ostream& os, tower_traffic_type ttt); enum tower_callback_type { - USER_REQUEST_DEPARTURE = 1, - USER_REQUEST_ARRIVAL = 2 + 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 }; // TODO - need some differentiation of IFR and VFR traffic in order to give the former priority. @@ -90,6 +92,9 @@ public: 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 + + bool vfrArrivalReported; + bool vfrArrivalAcknowledged; // Type of operation the plane is doing tower_traffic_type opType; @@ -121,7 +126,11 @@ public: void ReceiveUserCallback(int code); - void RequestLandingClearance(string ID); + // Contact tower for VFR approach + // eg "Cessna Charlie Foxtrot Golf Foxtrot Sierra eight miles South of the airport for full stop with Bravo" + // This function probably only called via user interaction - AI planes will have an overloaded function taking a planerec. + void VFRArrivalContact(string ID, LandingType opt = AIP_LT_UNKNOWN); + void RequestDepartureClearance(string ID); void ReportFinal(string ID); void ReportLongFinal(string ID); @@ -158,6 +167,8 @@ public: bool GetCrosswindConstraint(double& cpos); bool GetDownwindConstraint(double& dpos); bool GetBaseConstraint(double& bpos); + + string GenText(const string& m, int c); private: FGATCMgr* ATCmgr;