]> git.mxchange.org Git - flightgear.git/blobdiff - src/ATC/tower.cxx
any wind < 1kt is "calm", not just 0.0
[flightgear.git] / src / ATC / tower.cxx
index 1271e920a1b1ee85cd1bad46d53b1410158d82c6..fccaaaba16bfd0386b55b9fa3e6c0434d33b1732 100644 (file)
@@ -3,6 +3,7 @@
 // Written by David Luff, started March 2002.
 //
 // Copyright (C) 2002  David C. Luff - david.luff@nottingham.ac.uk
+// Copyright (C) 2008  Daniyar Atadjanov (ground clearance, gear check, weather, etc.)
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License as
@@ -16,7 +17,7 @@
 //
 // 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 #ifdef HAVE_CONFIG_H
 #  include <config.h>
 #  include <string.h>    // MSVC doesn't have strings.h
 #endif
 
+#include <sstream>
+#include <iomanip>
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/math/sg_random.h>
+#include <simgear/misc/sg_path.hxx>
+
 #include <Main/globals.hxx>
 #include <Airports/runways.hxx>
-#include <simgear/math/sg_geodesy.hxx>
-#include <simgear/debug/logstream.hxx>
 
 #include "tower.hxx"
-#include "ATCdisplay.hxx"
 #include "ATCmgr.hxx"
 #include "ATCutils.hxx"
 #include "ATCDialog.hxx"
 #include "commlist.hxx"
 #include "AILocalTraffic.hxx"
 
+
 SG_USING_STD(cout);
 
 // TowerPlaneRec
@@ -51,6 +58,7 @@ TowerPlaneRec::TowerPlaneRec() :
        clearedToLineUp(false),
        clearedToTakeOff(false),
        holdShortReported(false),
+       lineUpReported(false),
        downwindReported(false),
        longFinalReported(false),
        longFinalAcknowledged(false),
@@ -67,17 +75,20 @@ TowerPlaneRec::TowerPlaneRec() :
        opType(TTT_UNKNOWN),
        leg(LEG_UNKNOWN),
        landingType(AIP_LT_UNKNOWN),
+       gearWasUp(false),
+       gearUpReported(false),
        isUser(false)
 {
        plane.callsign = "UNKNOWN";
 }
 
-TowerPlaneRec::TowerPlaneRec(PlaneRec p) :
+TowerPlaneRec::TowerPlaneRec(const PlaneRec& p) :
        planePtr(NULL),
        clearedToLand(false),
        clearedToLineUp(false),
        clearedToTakeOff(false),
        holdShortReported(false),
+       lineUpReported(false),
        downwindReported(false),
        longFinalReported(false),
        longFinalAcknowledged(false),
@@ -94,17 +105,20 @@ TowerPlaneRec::TowerPlaneRec(PlaneRec p) :
        opType(TTT_UNKNOWN),
        leg(LEG_UNKNOWN),
        landingType(AIP_LT_UNKNOWN),
+       gearWasUp(false),
+       gearUpReported(false),
        isUser(false)
 {
        plane = p;
 }
 
-TowerPlaneRec::TowerPlaneRec(Point3D pt) :
+TowerPlaneRec::TowerPlaneRec(const Point3D& pt) :
        planePtr(NULL),
        clearedToLand(false),
        clearedToLineUp(false),
        clearedToTakeOff(false),
        holdShortReported(false),
+       lineUpReported(false),
        downwindReported(false),
        longFinalReported(false),
        longFinalAcknowledged(false),
@@ -121,18 +135,21 @@ TowerPlaneRec::TowerPlaneRec(Point3D pt) :
        opType(TTT_UNKNOWN),
        leg(LEG_UNKNOWN),
        landingType(AIP_LT_UNKNOWN),
+       gearWasUp(false),
+       gearUpReported(false),
        isUser(false)
 {
        plane.callsign = "UNKNOWN";
        pos = pt;
 }
 
-TowerPlaneRec::TowerPlaneRec(PlaneRec p, Point3D pt) :
+TowerPlaneRec::TowerPlaneRec(const PlaneRec& p, const Point3D& pt) :
        planePtr(NULL),
        clearedToLand(false),
        clearedToLineUp(false),
        clearedToTakeOff(false),
        holdShortReported(false),
+       lineUpReported(false),
        downwindReported(false),
        longFinalReported(false),
        longFinalAcknowledged(false),
@@ -149,6 +166,8 @@ TowerPlaneRec::TowerPlaneRec(PlaneRec p, Point3D pt) :
        opType(TTT_UNKNOWN),
        leg(LEG_UNKNOWN),
        landingType(AIP_LT_UNKNOWN),
+       gearWasUp(false),
+       gearUpReported(false),
        isUser(false)
 {
        plane = p;
@@ -191,7 +210,10 @@ FGTower::RemoveAllUserDialogOptions() really ought to be replaced by an ATCDialo
 At the moment planes in the lists are not guaranteed to always have a sensible ETA - it should be set as part of AddList functions, and lists should only be accessed this way. (FAIRLY MAJOR). 
 *******************************************/
 
-FGTower::FGTower() {
+FGTower::FGTower() :
+       separateGround(true),
+       ground(0)
+{
        ATCmgr = globals->get_ATC_mgr();
        
        _type = TOWER;
@@ -204,13 +226,11 @@ FGTower::FGTower() {
        update_count_max = 15;
        
        holdListItr = holdList.begin();
-       appList.clear();
        appListItr = appList.begin();
        depListItr = depList.begin();
        rwyListItr = rwyList.begin();
        circuitListItr = circuitList.begin();
        trafficListItr = trafficList.begin();
-       vacatedList.clear();
        vacatedListItr = vacatedList.begin();
        
        freqClear = true;
@@ -288,17 +308,25 @@ void FGTower::Init() {
                }
        }
        
+       RemoveAllUserDialogOptions();
+       
        // TODO - attempt to get a departure control pointer to see if we need to hand off departing traffic to departure.
        
        // Get the airport elevation
-       aptElev = dclGetAirportElev(ident.c_str());
+       aptElev = fgGetAirportElev(ident.c_str());
        
        // TODO - this function only assumes one active rwy.
        DoRwyDetails();
        
        // TODO - this currently assumes only one active runway.
        rwyOccupied = OnActiveRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0));
-       if(rwyOccupied) {
+       
+       if(!OnAnyRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), false)) {
+               //cout << ident << "  ADD 0\n";
+               current_atcdialog->add_entry(ident, "@AP Tower, @CS @MI miles @CD of the airport for full stop@AT",
+                               "Contact tower for VFR arrival (full stop)", TOWER,
+                               (int)USER_REQUEST_VFR_ARRIVAL_FULL_STOP);
+       } else {
                //cout << "User found on active runway\n";
                // Assume the user is started at the threshold ready to take-off
                TowerPlaneRec* t = new TowerPlaneRec;
@@ -309,17 +337,12 @@ void FGTower::Init() {
                t->leg = TAKEOFF_ROLL;
                t->isUser = true;
                t->planePtr = NULL;
-               t->clearedToTakeOff = true;
+               t->clearedToTakeOff = false;
                rwyList.push_back(t);
+               rwyListItr = rwyList.begin();
                departed = false;
-       } else {
-               //cout << "User not on active runway\n";
-               // For now assume that this means the user is not at the airport and is in the air.
-               // TODO FIXME - this will break when user starts on apron, at hold short, etc.
-               if(!OnAnyRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0))) {
-                       //cout << ident << "  ADD 0\n";
-                       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);
-               }
+               current_atcdialog->add_entry(ident, "@CS @TO", "Request departure / take-off clearance",
+                               TOWER, (int)USER_REQUEST_TAKE_OFF);
        }
 }
 
@@ -445,7 +468,7 @@ void FGTower::Update(double dt) {
 
 void FGTower::ReceiveUserCallback(int code) {
        if(code == (int)USER_REQUEST_VFR_DEPARTURE) {
-               //cout << "User requested departure\n";
+               RequestDepartureClearance("USER");
        } else if(code == (int)USER_REQUEST_VFR_ARRIVAL) {
                VFRArrivalContact("USER");
        } else if(code == (int)USER_REQUEST_VFR_ARRIVAL_FULL_STOP) {
@@ -461,6 +484,8 @@ void FGTower::ReceiveUserCallback(int code) {
                ReportRunwayVacated("USER");
        } else if(code == (int)USER_REPORT_GOING_AROUND) {
                ReportGoingAround("USER");
+       } else if(code == (int)USER_REQUEST_TAKE_OFF) {
+               RequestTakeOffClearance("USER");
        }
 }
 
@@ -477,16 +502,19 @@ void FGTower::Respond() {
                        string trns = t->plane.callsign;
                        trns += " ";
                        trns += name;
-                       trns += " Tower";
+                       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);
+                       float gp = fgGetFloat("/gear/gear/position-norm");
+                       if(gp < 1)
+                               t->gearWasUp = true; // This will be needed on final to tell "Gear down, ready to land."
                        if(op.y() < -1000) {
                                trns += " Report three mile straight-in runway ";
                                t->opType = STRAIGHT_IN;
                                if(t->isUser) {
-                                       current_atcdialog->add_entry(ident, "@AP Tower @CS @MI mile final Runway @RW", "Report Final", TOWER, (int)USER_REPORT_3_MILE_FINAL);
+                                       current_atcdialog->add_entry(ident, "@CS @MI mile final runway @RW@GR", "Report Final", TOWER, (int)USER_REPORT_3_MILE_FINAL);
                                } else {
                                        t->planePtr->RegisterTransmission(14);
                                }
@@ -501,14 +529,13 @@ void FGTower::Respond() {
                                t->opType = CIRCUIT;
                                // leave it in the app list until it gets into pattern though.
                                if(t->isUser) {
-                                       current_atcdialog->add_entry(ident, "@AP Tower @CS Downwind @RW", "Report Downwind", TOWER, (int)USER_REPORT_DOWNWIND);
+                                       current_atcdialog->add_entry(ident, "@AP Tower, @CS Downwind @RW", "Report Downwind", TOWER, (int)USER_REPORT_DOWNWIND);
                                } else {
                                        t->planePtr->RegisterTransmission(15);
                                }
                        }
                        trns += ConvertRwyNumToSpokenString(activeRwy);
                        if(_display) {
-                               //globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
                                pending_transmission = trns;
                                Transmit();
                        } else {
@@ -519,6 +546,41 @@ void FGTower::Respond() {
                        //cout << "Tower " << ident << " is responding to downwind reported...\n";
                        ProcessDownwindReport(t);
                        t->downwindReported = false;
+               } else if(t->lineUpReported) {
+                       string trns = t->plane.callsign;
+                       if(rwyOccupied) {
+                               double f = globals->get_ATC_mgr()->GetFrequency(ident, ATIS) / 100.0;
+                               string wtr;
+                               if(!f) {
+                                       wtr = ", " + GetWeather();
+                               }
+                               trns += " Cleared for take-off" + wtr;
+                               t->clearedToTakeOff = true;
+                       } else {
+                               if(!OnAnyRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), true)) {
+                                       // TODO: Check if any AI Planes on final and tell something like: "After the landing CALLSIGN line up runway two eight right"
+                                       trns += " Line up runway " + ConvertRwyNumToSpokenString(activeRwy);
+                                       t->clearedToTakeOff = false;
+                                       current_atcdialog->add_entry(ident, "@CS @TO", "Report ready for take-off", TOWER, (int)USER_REQUEST_TAKE_OFF);
+
+                               } else {
+                                       sg_srandom_time();
+                                       if((int(sg_random() * 10) + 1) != 3) {
+                                               t->clearedToTakeOff = true;
+                                               trns += " Cleared immediate take-off ";
+                                       } else {
+                                               t->clearedToTakeOff = false;
+                                               trns += " Negative, departure runway " + ConvertRwyNumToSpokenString(activeRwy);
+                                       }
+                               }
+                       }
+                       if(_display) {
+                               pending_transmission = trns;
+                               Transmit();
+                       } else {
+                               //cout << "Not displaying, trns was " << trns << '\n';
+                       }
+                       t->lineUpReported = false;
                } else if(t->holdShortReported) {
                        //cout << "Tower " << ident << " is reponding to holdShortReported...\n";
                        if(t->nextOnRwy) {
@@ -528,10 +590,11 @@ void FGTower::Respond() {
                                        ClearHoldingPlane(t);
                                        t->leg = TAKEOFF_ROLL;
                                        rwyList.push_back(t);
+                                       rwyListItr = rwyList.begin();
                                        rwyOccupied = true;
                                        // WARNING - WE ARE ASSUMING ONLY ONE PLANE REPORTING HOLD AT A TIME BELOW
                                        // FIXME TODO - FIX THIS!!!
-                                       if(holdList.size()) {
+                                       if(!holdList.empty()) {
                                                if(holdListItr == holdList.end()) {
                                                        holdListItr = holdList.begin();
                                                }
@@ -545,7 +608,6 @@ void FGTower::Respond() {
                                string trns = t->plane.callsign;
                                trns += " hold position";
                                if(_display) {
-                                       //globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
                                        pending_transmission = trns;
                                        Transmit();
                                }
@@ -562,7 +624,14 @@ void FGTower::Respond() {
                                if(t->landingType == FULL_STOP) {
                                        trns += " cleared to land ";
                                } else {
-                                       trns += " cleared for the option ";
+                                       double f = globals->get_ATC_mgr()->GetFrequency(ident, ATIS) / 100.0;
+                                       string wtr;
+                                       if(!f) {
+                                               wtr = ", " + GetWeather();
+                                       } else {
+                                               wtr = ", runway " + ConvertRwyNumToSpokenString(activeRwy);
+                                       }
+                                       trns += " cleared to land" + wtr;
                                }
                                // TODO - add winds
                                t->clearedToLand = true;
@@ -582,10 +651,18 @@ void FGTower::Respond() {
                                disp = false;
                        } else {
                                trns += " continue approach";
+                               trns += " and report ";
+                               trns += ((rwy.patternDirection == 1) ? "right " : "left ");
+                               trns += "downwind runway " + ConvertRwyNumToSpokenString(activeRwy);
+                               t->opType = CIRCUIT;
+                               if(t->isUser) {
+                                       current_atcdialog->add_entry(ident, "@AP Tower, @CS Downwind @RW", "Report Downwind", TOWER, (int)USER_REPORT_DOWNWIND);
+                               } else {
+                                       t->planePtr->RegisterTransmission(15);
+                               }
                                t->clearedToLand = false;
                        }
                        if(_display && disp) {
-                               //globals->get_ATC_display()->RegisterSingleMessage(trns);
                                pending_transmission = trns;
                                Transmit();
                        }
@@ -617,7 +694,7 @@ void FGTower::ProcessDownwindReport(TowerPlaneRec* t) {
        doThresholdETACalc();
        TowerPlaneRec* tf = NULL;
        for(tower_plane_rec_list_iterator twrItr = appList.begin(); twrItr != appList.end(); twrItr++) {
-               if((*twrItr)->eta < (t->eta + 45)) {
+               if((*twrItr)->eta < (t->eta + 45) && strcmp((*twrItr)->plane.callsign.c_str(), t->plane.callsign.c_str()) != 0) { // don't let ATC ask you to follow yourself
                        a++;
                        tf = *twrItr;
                        cf = true;
@@ -632,7 +709,7 @@ void FGTower::ProcessDownwindReport(TowerPlaneRec* t) {
        // This assumes that the number spoken is landing position, not circuit position, since some of the traffic might be on straight-in final.
        trns += " ";
        TowerPlaneRec* tt = NULL;
-       if((i == 1) && (!rwyList.size()) && (t->nextOnRwy) && (!cf)) {  // Unfortunately nextOnRwy currently doesn't handle circuit/straight-in ordering properly at present, hence the cf check below.
+       if((i == 1) && rwyList.empty() && (t->nextOnRwy) && (!cf)) {    // Unfortunately nextOnRwy currently doesn't handle circuit/straight-in ordering properly at present, hence the cf check below.
                trns += "Cleared to land";      // TODO - clear for the option if appropriate
                t->clearedToLand = true;
                if(!t->isUser) t->planePtr->RegisterTransmission(7);
@@ -656,7 +733,7 @@ void FGTower::ProcessDownwindReport(TowerPlaneRec* t) {
                trns += s;
                if((tt->opType) == CIRCUIT) {
                        PatternLeg leg;
-                       if(t->isUser) {
+                       if(tt->isUser) {
                                leg = tt->leg;
                        } else {
                                leg = tt->planePtr->GetLeg();
@@ -682,7 +759,6 @@ void FGTower::ProcessDownwindReport(TowerPlaneRec* t) {
                }
        }
        if(_display) {
-               //globals->get_ATC_display()->RegisterSingleMessage(trns);
                pending_transmission = trns;
                Transmit();
        }
@@ -711,12 +787,11 @@ void FGTower::ProcessRunwayVacatedReport(TowerPlaneRec* t) {
                if(!t->isUser) t->planePtr->RegisterTransmission(5);
        } else {
                // Cop-out!!
-               trns += " cleared for taxi to the GA parking";
+               trns += " cleared for taxi to general aviation parking";
                if(!t->isUser) t->planePtr->RegisterTransmission(6);    // TODO - this is a mega-hack!!
        }
        //cout << "trns = " << trns << '\n';
        if(_display) {
-               //globals->get_ATC_display()->RegisterSingleMessage(trns);
                pending_transmission = trns;
                Transmit();
        }
@@ -736,7 +811,7 @@ void FGTower::ClearHoldingPlane(TowerPlaneRec* t) {
        //if(departed plane < some threshold in time away) {
        if(0) {         // FIXME
        //if(timeSinceLastDeparture <= 60.0 && departed == true) {
-               trns += " line up";
+               trns += " line up runway " + ConvertRwyNumToSpokenString(activeRwy);
                t->clearedToLineUp = true;
                t->planePtr->RegisterTransmission(3);   // cleared to line-up
        //} else if(arriving plane < some threshold away) {
@@ -829,7 +904,6 @@ void FGTower::ClearHoldingPlane(TowerPlaneRec* t) {
                timeSinceLastDeparture = 0.0;
        }
        if(_display) {
-               //globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
                pending_transmission = trns;
                Transmit();
        }
@@ -843,7 +917,7 @@ void FGTower::ClearHoldingPlane(TowerPlaneRec* t) {
 // Do one plane from the hold list
 void FGTower::CheckHoldList(double dt) {
        //cout << "Entering CheckHoldList..." << endl;
-       if(holdList.size()) {
+       if(!holdList.empty()) {
                //cout << "*holdListItr = " << *holdListItr << endl;
                if(holdListItr == holdList.end()) {
                        holdListItr = holdList.begin();
@@ -859,16 +933,20 @@ void FGTower::CheckHoldList(double dt) {
                                //cout << "departed = " << departed << '\n';
                                //cout << "timeSinceLastDeparture = " << timeSinceLastDeparture << '\n';
                                if(rwyOccupied) {
-                                       // Do nothing
+                                       RemoveAllUserDialogOptions();
+                                       current_atcdialog->add_entry(ident, "@CS Ready for take-off", "Request take-off clearance", TOWER, (int)USER_REQUEST_TAKE_OFF);
                                } else if(timeSinceLastDeparture <= 60.0 && departed == true) {
                                        // Do nothing - this is a bit of a hack - should maybe do line up be ready here
                                } else {
                                        ClearHoldingPlane(t);
                                        t->leg = TAKEOFF_ROLL;
                                        rwyList.push_back(t);
+                                       rwyListItr = rwyList.begin();
                                        rwyOccupied = true;
                                        holdList.erase(holdListItr);
                                        holdListItr = holdList.begin();
+                                       if (holdList.empty())
+                                         return;
                                }
                        }
                        // TODO - rationalise the considerable code duplication above!
@@ -886,7 +964,7 @@ void FGTower::CheckCircuitList(double dt) {
        downwind_leg_pos = 0.0;
        crosswind_leg_pos = 0.0;
        
-       if(circuitList.size()) {        // Do one plane from the circuit
+       if(!circuitList.empty()) {      // Do one plane from the circuit
                if(circuitListItr == circuitList.end()) {
                        circuitListItr = circuitList.begin();
                }
@@ -928,18 +1006,21 @@ void FGTower::CheckCircuitList(double dt) {
                                                        t->opType = OUTBOUND;   // TODO - could check if the user has climbed significantly above circuit altitude as well.
                                                        // Since we are unknown operation we should be in depList already.
                                                        //cout << ident << " Removing user from circuitList (TTT_UNKNOWN)\n";
-                                                       circuitList.erase(circuitListItr);
+                                                       circuitListItr = circuitList.erase(circuitListItr);
                                                        RemoveFromTrafficList(t->plane.callsign);
-                                                       circuitListItr = circuitList.begin();
+                                                       if (circuitList.empty())
+                                                           return;
                                                }
                                        } else if(t->opType == CIRCUIT) {
                                                if(tortho.y() > 10000) {
                                                        // 10 km out - assume the user has abandoned the circuit!!
                                                        t->opType = OUTBOUND;
                                                        depList.push_back(t);
+                                                       depListItr = depList.begin();
                                                        //cout << ident << " removing user from circuitList (CIRCUIT)\n";
-                                                       circuitList.erase(circuitListItr);
-                                                       circuitListItr = circuitList.begin();
+                                                       circuitListItr = circuitList.erase(circuitListItr);
+                                                       if (circuitList.empty())
+                                                         return;
                                                }
                                        }
                                }
@@ -1058,18 +1139,29 @@ void FGTower::CheckCircuitList(double dt) {
                                // eg. is the plane accelerating down the runway taking off [OK],
                                // or stationary near the start [V. BAD!!].
                                // For now this should stop the AI plane landing on top of the user.
-                               string trns = t->plane.callsign;
-                               trns += " GO AROUND TRAFFIC ON RUNWAY I REPEAT GO AROUND";
-                               pending_transmission = trns;
-                               ImmediateTransmit();
-                               t->instructedToGoAround = true;
-                               t->clearedToLand = false;
-                               // Assume it complies!!!
-                               t->opType = CIRCUIT;
-                               t->leg = CLIMBOUT;
-                               if(t->planePtr) {
-                                       //cout << "Registering Go-around transmission with AI plane\n";
-                                       t->planePtr->RegisterTransmission(13);
+                               tower_plane_rec_list_iterator twrItr;
+                               twrItr = rwyList.begin();
+                               TowerPlaneRec* tpr = *twrItr;
+                               if(strcmp(tpr->plane.callsign.c_str(), t->plane.callsign.c_str()) == 0
+                                               && rwyList.size() == 1) {
+                                       // Fixing bug when ATC says that we must go around because of traffic on rwy
+                                       // but that traffic is our plane! In future we can use this expression
+                                       // for other ATC-messages like "On ground at 46, vacate left."
+
+                               } else {
+                                       string trns = t->plane.callsign;
+                                       trns += " GO AROUND TRAFFIC ON RUNWAY I REPEAT GO AROUND";
+                                       pending_transmission = trns;
+                                       ImmediateTransmit();
+                                       t->instructedToGoAround = true;
+                                       t->clearedToLand = false;
+                                       // Assume it complies!!!
+                                       t->opType = CIRCUIT;
+                                       t->leg = CLIMBOUT;
+                                       if(t->planePtr) {
+                                               //cout << "Registering Go-around transmission with AI plane\n";
+                                               t->planePtr->RegisterTransmission(13);
+                                       }
                                }
                        } else if(!t->clearedToLand) {
                                // The whip through the appList is a hack since currently t->nextOnRwy doesn't always work
@@ -1109,6 +1201,8 @@ void FGTower::CheckCircuitList(double dt) {
                        circuitListItr = circuitList.erase(circuitListItr);
                        if(circuitListItr == circuitList.end() ) {
                                circuitListItr = circuitList.begin();
+                               // avoid increment of circuitListItr (would increment to second element, or crash if no element left)
+                               return;
                        }
                }
                ++circuitListItr;
@@ -1150,20 +1244,24 @@ void FGTower::CheckRunwayList(double dt) {
                                        // It's possible we could be a bit more proactive about this.
                                } else if(t->opType == OUTBOUND) {
                                        depList.push_back(t);
+                                       depListItr = depList.begin();
                                        rwyList.pop_front();
                                        departed = true;
                                        timeSinceLastDeparture = 0.0;
                                } else if(t->opType == CIRCUIT) {
                                        //cout << ident << " adding " << t->plane.callsign << " to circuitList" << endl;
                                        circuitList.push_back(t);
+                                       circuitListItr = circuitList.begin();
                                        AddToTrafficList(t);
                                        rwyList.pop_front();
                                        departed = true;
                                        timeSinceLastDeparture = 0.0;
                                } else if(t->opType == TTT_UNKNOWN) {
                                        depList.push_back(t);
+                                       depListItr = depList.begin();
                                        //cout << ident << " adding " << t->plane.callsign << " to circuitList" << endl;
                                        circuitList.push_back(t);
+                                       circuitListItr = circuitList.begin();
                                        AddToTrafficList(t);
                                        rwyList.pop_front();
                                        departed = true;
@@ -1181,7 +1279,7 @@ void FGTower::CheckRunwayList(double dt) {
 void FGTower::CheckApproachList(double dt) {
        //cout << "CheckApproachList called for " << ident << endl;
        //cout << "AppList.size is " << appList.size() << endl;
-       if(appList.size()) {
+       if(!appList.empty()) {
                if(appListItr == appList.end()) {
                        appListItr = appList.begin();
                }
@@ -1197,29 +1295,56 @@ void FGTower::CheckApproachList(double dt) {
                }
                doThresholdETACalc();   // We need this here because planes in the lists are not guaranteed to *always* have the correct ETA
                //cout << "eta is " << t->eta << ", rwy is " << (rwyList.size() ? "occupied " : "clear ") << '\n';
+               Point3D tortho = ortho.ConvertToLocal(t->pos);
                if(t->eta < 12 && rwyList.size() && !(t->instructedToGoAround)) {
                        // TODO - need to make this more sophisticated 
                        // eg. is the plane accelerating down the runway taking off [OK],
                        // or stationary near the start [V. BAD!!].
                        // For now this should stop the AI plane landing on top of the user.
-                       string trns = t->plane.callsign;
-                       trns += " GO AROUND TRAFFIC ON RUNWAY I REPEAT GO AROUND";
-                       pending_transmission = trns;
-                       ImmediateTransmit();
-                       t->instructedToGoAround = true;
-                       t->clearedToLand = false;
-                       t->nextOnRwy = false;   // But note this is recalculated so don't rely on it
-                       // Assume it complies!!!
-                       t->opType = CIRCUIT;
-                       t->leg = CLIMBOUT;
-                       if(!t->isUser) {
-                               if(t->planePtr) {
-                                       //cout << "Registering Go-around transmission with AI plane\n";
-                                       t->planePtr->RegisterTransmission(13);
-                               }
+                       tower_plane_rec_list_iterator twrItr;
+                       twrItr = rwyList.begin();
+                       TowerPlaneRec* tpr = *twrItr;
+                       if(strcmp ( tpr->plane.callsign.c_str(), t->plane.callsign.c_str() ) == 0 && rwyList.size() == 1) {
+                                       // Fixing bug when ATC says that we must go around because of traffic on rwy
+                                       // but that traffic is we! In future we can use this expression
+                                       // for other ATC-messages like "On ground at 46, vacate left."
+
                        } else {
-                               // TODO - add Go-around ack to comm options,
-                               // remove report rwy vacated. (possibly).
+                               string trns = t->plane.callsign;
+                               trns += " GO AROUND TRAFFIC ON RUNWAY I REPEAT GO AROUND";
+                               pending_transmission = trns;
+                               ImmediateTransmit();
+                               t->instructedToGoAround = true;
+                               t->clearedToLand = false;
+                               t->nextOnRwy = false;   // But note this is recalculated so don't rely on it
+                               // Assume it complies!!!
+                               t->opType = CIRCUIT;
+                               t->leg = CLIMBOUT;
+                               if(!t->isUser) {
+                                       if(t->planePtr) {
+                                               //cout << "Registering Go-around transmission with AI plane\n";
+                                               t->planePtr->RegisterTransmission(13);
+                                       }
+                               } else {
+                                       // TODO - add Go-around ack to comm options,
+                                       // remove report rwy vacated. (possibly).
+                               }
+                       }
+               } else if(t->isUser && t->eta < 90 && tortho.y() > -2500 && t->clearedToLand && t->gearUpReported == false) {
+                       // Check if gear up or down
+                       double gp = fgGetFloat("/gear/gear/position-norm");
+                       if(gp < 1) {
+                               string trnsm = t->plane.callsign;
+                               sg_srandom_time();
+                               int rnd = int(sg_random() * 2) + 1;
+                               if(rnd == 2) {                          // Random message for more realistic ATC ;)
+                                       trnsm += ", LANDING GEAR APPEARS UP!";
+                               } else {
+                                       trnsm += ", Check wheels down and locked.";
+                               }
+                               pending_transmission = trnsm;
+                               ImmediateTransmit();
+                               t->gearUpReported = true;
                        }
                } else if(t->eta < 90 && !t->clearedToLand) {
                        //doThresholdETACalc();
@@ -1279,6 +1404,9 @@ void FGTower::CheckApproachList(double dt) {
                        if(appListItr == appList.end() ) {
                                appListItr = appList.begin();
                        }
+                       if (appList.empty())
+                         return;
+
                }
                
                ++appListItr;
@@ -1288,7 +1416,7 @@ void FGTower::CheckApproachList(double dt) {
 
 // Do one plane from the departure list
 void FGTower::CheckDepartureList(double dt) {
-       if(depList.size()) {
+       if(!depList.empty()) {
                if(depListItr == depList.end()) {
                        depListItr = depList.begin();
                }
@@ -1299,6 +1427,12 @@ void FGTower::CheckDepartureList(double dt) {
                if(t->isUser) distout = dclGetHorizontalSeparation(Point3D(lon, lat, elev), Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), user_elev_node->getDoubleValue()));
                else distout = dclGetHorizontalSeparation(Point3D(lon, lat, elev), t->planePtr->GetPos());
                //cout << " distout = " << distout << '\n';
+               if(t->isUser && !(t->clearedToTakeOff)) {       // HACK - we use clearedToTakeOff to check if ATC already contacted with plane (and cleared take-off) or not
+                       if(!OnAnyRunway(Point3D(user_lon_node->getDoubleValue(), user_lat_node->getDoubleValue(), 0.0), false)) {
+                               current_atcdialog->remove_entry(ident, USER_REQUEST_TAKE_OFF, TOWER);
+                               t->clearedToTakeOff = true;     // FIXME
+                       }
+               }
                if(distout > 10000) {
                        string trns = t->plane.callsign;
                        trns += " You are now clear of my airspace, good day";
@@ -1308,7 +1442,7 @@ void FGTower::CheckDepartureList(double dt) {
                                // Change the communication options
                                RemoveAllUserDialogOptions();
                                //cout << "ADD A\n";
-                               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);
+                               current_atcdialog->add_entry(ident, "@AP Tower, @CS @MI miles @CD of the airport for full stop@AT", "Contact tower for VFR arrival (full stop)", TOWER, (int)USER_REQUEST_VFR_ARRIVAL_FULL_STOP);
                        } else {
                                // Send a clear-of-airspace signal
                                // TODO - implement this once we actually have departing AI traffic (currently all circuits or arrivals).
@@ -1334,6 +1468,7 @@ void FGTower::RemoveAllUserDialogOptions() {
        current_atcdialog->remove_entry(ident, USER_REPORT_DOWNWIND, TOWER);
        current_atcdialog->remove_entry(ident, USER_REPORT_RWY_VACATED, TOWER);
        current_atcdialog->remove_entry(ident, USER_REPORT_GOING_AROUND, TOWER);        
+       current_atcdialog->remove_entry(ident, USER_REQUEST_TAKE_OFF, TOWER);
 }
 
 // Returns true if positions of crosswind/downwind/base leg turns should be constrained by previous traffic
@@ -1435,7 +1570,7 @@ void FGTower::DoRwyDetails() {
 
 // Figure out if a given position lies on the active runway
 // Might have to change when we consider more than one active rwy.
-bool FGTower::OnActiveRunway(Point3D pt) {
+bool FGTower::OnActiveRunway(const Point3D& pt) {
        // TODO - check that the centre calculation below isn't confused by displaced thesholds etc.
        Point3D xyc((rwy.end1ortho.x() + rwy.end2ortho.x())/2.0, (rwy.end1ortho.y() + rwy.end2ortho.y())/2.0, 0.0);
        Point3D xyp = ortho.ConvertToLocal(pt);
@@ -1449,17 +1584,17 @@ bool FGTower::OnActiveRunway(Point3D pt) {
        double wdiff = fabs(xyp.x() - xyc.x());
 
        return((ldiff < rlen) && (wdiff < rwidth));
-}      
-
+}
 
 // Figure out if a given position lies on any runway or not
 // Only call this at startup - reading the runways database is expensive and needs to be fixed!
-bool FGTower::OnAnyRunway(Point3D pt) {
+bool FGTower::OnAnyRunway(const Point3D& pt, bool onGround) {
        ATCData ad;
-       double dist = current_commlist->FindClosest(lon, lat, elev, ad, TOWER, 10.0);
+       double dist = current_commlist->FindClosest(lon, lat, elev, ad, TOWER, 7.0);
        if(dist < 0.0) {
                return(false);
        }
+       
        // Based on the airport-id, go through all the runways and check for a point in them
        
        // TODO - do we actually need to search for the airport - surely we already know our ident and
@@ -1471,18 +1606,23 @@ bool FGTower::OnAnyRunway(Point3D pt) {
                SG_LOG(SG_ATC, SG_WARN, "Unable to find any runways for airport ID " << ad.ident << " in FGTower");
        }
        bool on = false;
-       while(runway._id == ad.ident) {         
+       while(runway._id == ad.ident) {
                on = OnRunway(pt, runway);
                //cout << "Runway " << runway._rwy_no << ": On = " << (on ? "true\n" : "false\n");
-               if(on) return(true);
-               globals->get_runways()->next(&runway);          
+               if(on) {
+                       if(onGround == false)
+                               return(true);
+                       if(runway._rwy_no != "xx")
+                               return(true);
+               }
+               globals->get_runways()->next(&runway);
        }
        return(on);
 }
 
 
 // Returns true if successful
-bool FGTower::RemoveFromTrafficList(string id) {
+bool FGTower::RemoveFromTrafficList(const string& id) {
        tower_plane_rec_list_iterator twrItr;
        for(twrItr = trafficList.begin(); twrItr != trafficList.end(); twrItr++) {
                TowerPlaneRec* tpr = *twrItr;
@@ -1498,7 +1638,7 @@ bool FGTower::RemoveFromTrafficList(string id) {
 
 
 // Returns true if successful
-bool FGTower::RemoveFromAppList(string id) {
+bool FGTower::RemoveFromAppList(const string& id) {
        tower_plane_rec_list_iterator twrItr;
        for(twrItr = appList.begin(); twrItr != appList.end(); twrItr++) {
                TowerPlaneRec* tpr = *twrItr;
@@ -1513,7 +1653,7 @@ bool FGTower::RemoveFromAppList(string id) {
 }
 
 // Returns true if successful
-bool FGTower::RemoveFromRwyList(string id) {
+bool FGTower::RemoveFromRwyList(const string& id) {
        tower_plane_rec_list_iterator twrItr;
        for(twrItr = rwyList.begin(); twrItr != rwyList.end(); twrItr++) {
                TowerPlaneRec* tpr = *twrItr;
@@ -1641,10 +1781,12 @@ bool FGTower::AddToCircuitList(TowerPlaneRec* t) {
                        // It depends on what the two planes are doing and whether there's a conflict what we do.
                        if(tpr->eta - t->eta > separation_time) {       // No probs, plane 2 can squeeze in before plane 1 with no apparent conflict
                                circuitList.insert(twrItr, t);
+                               circuitListItr = circuitList.begin();
                        } else {        // Ooops - this ones tricky - we have a potential conflict!
                                conflict = true;
                                // HACK - just add anyway for now and flag conflict.
                                circuitList.insert(twrItr, t);
+                               circuitListItr = circuitList.begin();
                        }
                        //cout << "\tC\t" << circuitList.size() << '\n';
                        return(conflict);
@@ -1653,6 +1795,7 @@ bool FGTower::AddToCircuitList(TowerPlaneRec* t) {
        // If we get here we must be at the end of the list, or maybe the list is empty.
        //cout << ident << " adding " << t->plane.callsign << " to circuitList" << endl;
        circuitList.push_back(t);       // TODO - check the separation with the preceding plane for the conflict flag.
+       circuitListItr = circuitList.begin();
        //cout << "\tE\t" << circuitList.size() << endl;
        return(conflict);
 }
@@ -1670,6 +1813,15 @@ void FGTower::AddToVacatedList(TowerPlaneRec* t) {
        vacatedList.push_back(t);
 }
 
+void FGTower::AddToHoldingList(TowerPlaneRec* t) {
+       tower_plane_rec_list_iterator it, end = holdList.end();
+       for (it = holdList.begin(); it != end; ++it) {
+               if ((*it)->plane.callsign == t->plane.callsign)
+                       return;
+       
+               holdList.push_back(t);
+       }
+}
 
 // Calculate the eta of a plane to the threshold.
 // For ground traffic this is the fastest they can get there.
@@ -1900,7 +2052,7 @@ double FGTower::GetTrafficETA(unsigned int list_pos, bool printout) {
 }
        
 
-void FGTower::ContactAtHoldShort(PlaneRec plane, FGAIPlane* requestee, tower_traffic_type operation) {
+void FGTower::ContactAtHoldShort(const PlaneRec& plane, FGAIPlane* requestee, tower_traffic_type operation) {
        // HACK - assume that anything contacting at hold short is new for now - FIXME LATER
        TowerPlaneRec* t = new TowerPlaneRec;
        t->plane = plane;
@@ -1934,7 +2086,7 @@ void FGTower::ContactAtHoldShort(PlaneRec plane, FGAIPlane* requestee, tower_tra
 
 // Register the presence of an AI plane at a point where contact would already have been made in real life
 // CAUTION - currently it is assumed that this plane's callsign is unique - it is up to AIMgr to generate unique callsigns.
-void FGTower::RegisterAIPlane(PlaneRec plane, FGAIPlane* ai, tower_traffic_type op, PatternLeg lg) {
+void FGTower::RegisterAIPlane(const PlaneRec& plane, FGAIPlane* ai, const tower_traffic_type& op, const PatternLeg& lg) {
        // At the moment this is only going to be tested with inserting an AI plane on downwind
        TowerPlaneRec* t = new TowerPlaneRec;
        t->plane = plane;
@@ -1954,7 +2106,7 @@ void FGTower::RegisterAIPlane(PlaneRec plane, FGAIPlane* ai, tower_traffic_type
        doThresholdUseOrder();
 }
 
-void FGTower::DeregisterAIPlane(string id) {
+void FGTower::DeregisterAIPlane(const string& id) {
        RemovePlane(id);
 }
 
@@ -1962,7 +2114,7 @@ void FGTower::DeregisterAIPlane(string id) {
 // 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) {
+void FGTower::VFRArrivalContact(const string& ID, const LandingType& opt) {
        //cout << "USER Request Landing Clearance called for ID " << ID << '\n';
        
        // For now we'll assume that the user is a light plane and can get him/her to join the circuit if necessary.
@@ -2005,6 +2157,7 @@ void FGTower::VFRArrivalContact(string ID, LandingType opt) {
        responseReqd = true;
        
        appList.push_back(t);   // Not necessarily permanent
+       appListItr = appList.begin();
        AddToTrafficList(t);
        
        current_atcdialog->remove_entry(ident, USER_REQUEST_VFR_ARRIVAL, TOWER);
@@ -2013,7 +2166,7 @@ void FGTower::VFRArrivalContact(string ID, LandingType opt) {
 }
 
 // landingType defaults to AIP_LT_UNKNOWN
-void FGTower::VFRArrivalContact(PlaneRec plane, FGAIPlane* requestee, LandingType lt) {
+void FGTower::VFRArrivalContact(const PlaneRec& plane, FGAIPlane* requestee, const LandingType& lt) {
        //cout << "VFRArrivalContact called for plane " << plane.callsign << " at " << ident << '\n';
        // Possible hack - assume this plane is new for now - TODO - should check really
        TowerPlaneRec* t = new TowerPlaneRec;
@@ -2034,21 +2187,42 @@ void FGTower::VFRArrivalContact(PlaneRec plane, FGAIPlane* requestee, LandingTyp
        
        //cout << "Before adding, appList.size = " << appList.size() << " at " << ident << '\n';
        appList.push_back(t);   // Not necessarily permanent
+       appListItr = appList.begin();
        //cout << "After adding, appList.size = " << appList.size() << " at " << ident << '\n';
        AddToTrafficList(t);
 }
 
-void FGTower::RequestDepartureClearance(string ID) {
+void FGTower::RequestDepartureClearance(const string& ID) {
        //cout << "Request Departure Clearance called...\n";
 }
+
+void FGTower::RequestTakeOffClearance(const string& ID) {
+       string uid=ID;
+       if(ID == "USER") {
+               uid = fgGetString("/sim/user/callsign");
+               current_atcdialog->remove_entry(ident, USER_REQUEST_TAKE_OFF, TOWER);
+       }
+       TowerPlaneRec* t = FindPlane(uid);
+       if(t) {
+               if(!(t->clearedToTakeOff)) {
+                       departed = false;
+                       t->lineUpReported=true;
+                       responseReqd = true;
+               }
+       }
+       else {
+               SG_LOG(SG_ATC, SG_WARN, "WARNING: Unable to find plane " << ID << " in FGTower::RequestTakeOffClearance(...)");
+       }
+}
        
-void FGTower::ReportFinal(string ID) {
+void FGTower::ReportFinal(const string& ID) {
        //cout << "Report Final Called at tower " << ident << " by plane " << ID << '\n';
+       string uid=ID;
        if(ID == "USER") {
-               ID = fgGetString("/sim/user/callsign");
+               uid = fgGetString("/sim/user/callsign");
                current_atcdialog->remove_entry(ident, USER_REPORT_3_MILE_FINAL, TOWER);
        }
-       TowerPlaneRec* t = FindPlane(ID);
+       TowerPlaneRec* t = FindPlane(uid);
        if(t) {
                t->finalReported = true;
                t->finalAcknowledged = false;
@@ -2065,12 +2239,13 @@ void FGTower::ReportFinal(string ID) {
        }
 }
 
-void FGTower::ReportLongFinal(string ID) {
+void FGTower::ReportLongFinal(const string& ID) {
+       string uid=ID;
        if(ID == "USER") {
-               ID = fgGetString("/sim/user/callsign");
+               uid = fgGetString("/sim/user/callsign");
                current_atcdialog->remove_entry(ident, USER_REPORT_3_MILE_FINAL, TOWER);
        }
-       TowerPlaneRec* t = FindPlane(ID);
+       TowerPlaneRec* t = FindPlane(uid);
        if(t) {
                t->longFinalReported = true;
                t->longFinalAcknowledged = false;
@@ -2086,13 +2261,14 @@ void FGTower::ReportLongFinal(string ID) {
 //void FGTower::ReportMiddleMarker(string ID);
 //void FGTower::ReportInnerMarker(string ID);
 
-void FGTower::ReportRunwayVacated(string ID) {
+void FGTower::ReportRunwayVacated(const string& ID) {
        //cout << "Report Runway Vacated Called at tower " << ident << " by plane " << ID << '\n';
+       string uid=ID;
        if(ID == "USER") {
-               ID = fgGetString("/sim/user/callsign");
+               uid = fgGetString("/sim/user/callsign");
                current_atcdialog->remove_entry(ident, USER_REPORT_RWY_VACATED, TOWER);
        }
-       TowerPlaneRec* t = FindPlane(ID);
+       TowerPlaneRec* t = FindPlane(uid);
        if(t) {
                //cout << "Found it...\n";
                t->rwyVacatedReported = true;
@@ -2104,7 +2280,7 @@ void FGTower::ReportRunwayVacated(string ID) {
        }
 }
 
-TowerPlaneRec* FGTower::FindPlane(string ID) {
+TowerPlaneRec* FGTower::FindPlane(const string& ID) {
        //cout << "FindPlane called for " << ID << "...\n";
        tower_plane_rec_list_iterator twrItr;
        // Do the approach list first
@@ -2137,72 +2313,83 @@ TowerPlaneRec* FGTower::FindPlane(string ID) {
        return(NULL);
 }
 
-void FGTower::RemovePlane(string ID) {
+void FGTower::RemovePlane(const string& ID) {
        //cout << ident << " RemovePlane called for " << ID << '\n';
        // We have to be careful here - we want to erase the plane from all lists it is in,
        // but we can only delete it once, AT THE END.
        TowerPlaneRec* t = NULL;
        tower_plane_rec_list_iterator twrItr;
-       for(twrItr = appList.begin(); twrItr != appList.end(); twrItr++) {
+       for(twrItr = appList.begin(); twrItr != appList.end();) {
                if((*twrItr)->plane.callsign == ID) {
                        t = *twrItr;
                        twrItr = appList.erase(twrItr);
                        appListItr = appList.begin();
-               }
+                       // HACK: aircraft are sometimes more than once in a list, so we need to
+                       // remove them all before we can delete the TowerPlaneRec class
+                       //break;
+               } else
+                        ++twrItr;
        }
-       for(twrItr = depList.begin(); twrItr != depList.end(); twrItr++) {
+       for(twrItr = depList.begin(); twrItr != depList.end();) {
                if((*twrItr)->plane.callsign == ID) {
                        t = *twrItr;
                        twrItr = depList.erase(twrItr);
                        depListItr = depList.begin();
-               }
+               } else
+                        ++twrItr;
        }
-       for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) {
+       for(twrItr = circuitList.begin(); twrItr != circuitList.end();) {
                if((*twrItr)->plane.callsign == ID) {
                        t = *twrItr;
                        twrItr = circuitList.erase(twrItr);
                        circuitListItr = circuitList.begin();
-               }
+                } else
+                        ++twrItr;
        }
-       for(twrItr = holdList.begin(); twrItr != holdList.end(); twrItr++) {
+       for(twrItr = holdList.begin(); twrItr != holdList.end();) {
                if((*twrItr)->plane.callsign == ID) {
                        t = *twrItr;
                        twrItr = holdList.erase(twrItr);
                        holdListItr = holdList.begin();
-               }
+                } else
+                        ++twrItr;
        }
-       for(twrItr = rwyList.begin(); twrItr != rwyList.end(); twrItr++) {
+       for(twrItr = rwyList.begin(); twrItr != rwyList.end();) {
                if((*twrItr)->plane.callsign == ID) {
                        t = *twrItr;
                        twrItr = rwyList.erase(twrItr);
                        rwyListItr = rwyList.begin();
-               }
+                } else
+                        ++twrItr;
        }
-       for(twrItr = vacatedList.begin(); twrItr != vacatedList.end(); twrItr++) {
+       for(twrItr = vacatedList.begin(); twrItr != vacatedList.end();) {
                if((*twrItr)->plane.callsign == ID) {
                        t = *twrItr;
                        twrItr = vacatedList.erase(twrItr);
                        vacatedListItr = vacatedList.begin();
-               }
+                } else
+                        ++twrItr;
        }
-       for(twrItr = trafficList.begin(); twrItr != trafficList.end(); twrItr++) {
+       for(twrItr = trafficList.begin(); twrItr != trafficList.end();) {
                if((*twrItr)->plane.callsign == ID) {
                        t = *twrItr;
                        twrItr = trafficList.erase(twrItr);
                        trafficListItr = trafficList.begin();
-               }
+                } else
+                        ++twrItr;
        }
-       // And finally, delete the record if we found it.
-       if(t) delete t;
+       // And finally, delete the record.
+       delete t;
 }
 
-void FGTower::ReportDownwind(string ID) {
+void FGTower::ReportDownwind(const string& ID) {
        //cout << "ReportDownwind(...) called\n";
+       string uid=ID;
        if(ID == "USER") {
-               ID = fgGetString("/sim/user/callsign");
+               uid = fgGetString("/sim/user/callsign");
                current_atcdialog->remove_entry(ident, USER_REPORT_DOWNWIND, TOWER);
        }
-       TowerPlaneRec* t = FindPlane(ID);
+       TowerPlaneRec* t = FindPlane(uid);
        if(t) {
                t->downwindReported = true;
                responseReqd = true;
@@ -2226,13 +2413,14 @@ void FGTower::ReportDownwind(string ID) {
        }
 }
 
-void FGTower::ReportGoingAround(string ID) {
+void FGTower::ReportGoingAround(const string& ID) {
+       string uid=ID;
        if(ID == "USER") {
-               ID = fgGetString("/sim/user/callsign");
+               uid = fgGetString("/sim/user/callsign");
                RemoveAllUserDialogOptions();   // TODO - it would be much more efficient if ATCDialog simply had a clear() function!!!
-               current_atcdialog->add_entry(ident, "@AP Tower @CS Downwind @RW", "Report Downwind", TOWER, (int)USER_REPORT_DOWNWIND);
+               current_atcdialog->add_entry(ident, "@AP Tower, @CS Downwind @RW", "Report Downwind", TOWER, (int)USER_REPORT_DOWNWIND);
        }
-       TowerPlaneRec* t = FindPlane(ID);
+       TowerPlaneRec* t = FindPlane(uid);
        if(t) {
                //t->goAroundReported = true;  // No need to set this until we start responding to it.
                responseReqd = false;   // might change in the future but for now we'll not distract them during the go-around.
@@ -2266,6 +2454,7 @@ string FGTower::GenText(const string& m, int c) {
        int len;
        //FGTransmission t;
        string usercall = fgGetString("/sim/user/callsign");
+       TowerPlaneRec* t = FindPlane(responseID);
        
        //transmission_list_type     tmissions = transmissionlist_station[station];
        //transmission_list_iterator current   = tmissions.begin();
@@ -2282,6 +2471,7 @@ string FGTower::GenText(const string& m, int c) {
                        
                        // Replace all the '@' parameters with the actual text.
                        int check = 0;  // If mes gets overflowed the while loop can go infinite
+                       double gp = fgGetFloat("/gear/gear/position-norm");
                        while ( strchr(&mes[0], crej) != NULL  ) {      // ie. loop until no more occurances of crej ('@') found
                                pos = strchr( &mes[0], crej );
                                memmove(&tag[0], pos, 3);
@@ -2325,6 +2515,19 @@ string FGTower::GenText(const string& m, int c) {
                                        strcat( &dum[0], &buf[0] );
                                        */
                                }
+                               else if ( strcmp ( tag, "@AT" ) == 0 ) {        // ATIS ID
+                                       /*
+                                       char buf[10];
+                                       sprintf( buf, "%i", (int)(tpars.heading) );
+                                       strcat( &dum[0], &buf[0] );
+                                       */
+                                       double f = globals->get_ATC_mgr()->GetFrequency(ident, ATIS) / 100.0;
+                                       if(f) {
+                                               string atis_id;
+                                               atis_id = ", information " + GetATISID();
+                                               strcat( &dum[0], atis_id.c_str() );
+                                       }
+                               }
                                else if ( strcmp ( tag, "@VD" ) == 0 ) {
                                        /*
                                        if ( tpars.VDir == 1 ) {
@@ -2348,6 +2551,20 @@ string FGTower::GenText(const string& m, int c) {
                                        strcat( &dum[0], &buf[0] );
                                        */
                                }
+                               else if ( strcmp ( tag, "@TO" ) == 0 ) {      // Requesting take-off or departure clearance
+                                       string tmp;
+                                       if (rwyOccupied) {
+                                               tmp = "Ready for take-off";
+                                       } else {
+                                               if (OnAnyRunway(Point3D(user_lon_node->getDoubleValue(),
+                                                               user_lat_node->getDoubleValue(), 0.0),true)) {
+                                                       tmp = "Request take-off clearance";
+                                               } else {
+                                                       tmp = "Request departure clearance";
+                                               }
+                                       }
+                                       strcat(&dum[0], tmp.c_str());
+                               }
                                else if ( strcmp ( tag, "@MI" ) == 0 ) {
                                        char buf[10];
                                        //sprintf( buf, "%3.1f", tpars.miles );
@@ -2364,7 +2581,13 @@ string FGTower::GenText(const string& m, int c) {
                                }
                                else if ( strcmp ( tag, "@RW" ) == 0 ) {
                                        strcat(&dum[0], ConvertRwyNumToSpokenString(activeRwy).c_str());
-                               } else if(strcmp(tag, "@CD") == 0) {    // @CD = compass direction
+                               }
+                               else if ( strcmp ( tag, "@GR" ) == 0 ) {        // Gear position (on final)
+                                       if(t->gearWasUp && gp > 0.99) {
+                                               strcat(&dum[0], ", gear down, ready to land.");
+                                       }
+                               }
+                               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;
@@ -2403,8 +2626,36 @@ string FGTower::GenText(const string& m, int c) {
                        //break;
                //}
        //}
-       if ( mes != "" ) return mes;
-       else return "No transmission found";
+       return mes[0] ? mes : "No transmission found";
+}
+
+string FGTower::GetWeather() {
+       std::ostringstream msg;
+
+       // wind
+       double hdg = wind_from_hdg->getDoubleValue();
+       double speed = wind_speed_knots->getDoubleValue();
+       if (speed < 1)
+               msg << "wind calm";
+       else
+               msg << "wind " << int(hdg) << " degrees at " << int(speed) << " knots";
+
+       // visibility
+       double visibility = fgGetDouble("/environment/visibility-m");
+       if (visibility < 10000)
+               msg << ", visibility " << int(visibility / 1609) << " miles";
+
+       // pressure / altimeter
+       double pressure = fgGetDouble("/environment/pressure-sea-level-inhg");
+       msg << ", QFE " << fixed << setprecision(2) << pressure << ".";
+
+       return msg.str();
+}
+
+string FGTower::GetATISID() {
+       int hours = fgGetInt("/sim/time/utc/hour");
+       int phonetic_id = current_commlist->GetCallSign(ident, hours, 0);
+       return GetPhoneticIdent(phonetic_id);
 }
 
 ostream& operator << (ostream& os, tower_traffic_type ttt) {