]> git.mxchange.org Git - flightgear.git/commitdiff
Daniyar ATADJANOV:
authormfranz <mfranz>
Thu, 31 Jan 2008 22:33:32 +0000 (22:33 +0000)
committermfranz <mfranz>
Thu, 31 Jan 2008 22:33:32 +0000 (22:33 +0000)
This patch contains bugfixes from my previous patch
 (i made mistakes when using "diff" utility last time):

    1. Now ATC do not tells us to go around because of
       traffic on RWy, when that traffic is WE.
    2. Now ATC don't say that we must follow ourself
       (like "GFS, number two, follow GFS on final").

And new Tower-ATC features:

    1. Requesting departure clearance. When you are
       on taxiway, you can request departure clearance.
       Tower will answer with something like:
       "Line up runway two eight right".

       TODO: check if any AI-Planes on final and tell
       something like: "After the landing AI-CALLSIGN
       line up runway two eight right".

    2. Requesting take-off clearance. If you are on
       runway, you can tell to ATC that you are ready
       for take-off. So, Tower will tell you that you
       are cleared for take-off (or immediate take-off).

    3. Reporting landing gear position on final by pilot.
       If you are on aircraft, which landing gear can
       be UP, when reporting final you will tell
       "Gear down, ready to land".

    4. Reporting landing gear position on final by ATC.
       When i was newbie and was flying on Cessna 310, i
       ALWAYS forgot landing gears on final. Now if you are on
       final and forgot landing gear, Tower will tell you:
       "LANDING GEAR APPEARS UP" or "Check wheels down and
       locked". So you will have some time to push gear
       down or execute missed approach ;)

    5. ATIS phonetic ID in requesting landing message.
       In FG pilots says: "for full stop with ATIS" everytime
       (when airport doesn't have ATIS too). But in real life
       pilot saying phonetic ID of ATIS message. Now we have:
       "for full stop, information alpha ... / ... zulu".
       If airport doesn't have ATIS, pilot don't say this.

       TODO: add this feature to AI-Planes too.

     6. Advising weather condition in airport by Tower.
        If you are requesting take-off or landing in airport
        that doesn't have ATIS service, Tower will tell you
        about wind direction and speed, visibility and QFE.

     7. Reporting downwind if missed approach. When some AI-Plane
        is on final FG's ATC tell you to "continue approach".
        And then you lost radio transmission. Now ATC says:
        "continue approach and report left/right downwind".
        And now we have "Report Downwind" entry (button)
        in "ATC Communication" form.

src/ATC/tower.cxx
src/ATC/tower.hxx

index b9982546120ff8aa0f28a6c7c2eebdfd3b5fc560..d499ff0269630e4fe5a6c9b973880ccd774c84b5 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
 #  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 "ATCmgr.hxx"
@@ -40,6 +47,7 @@
 #include "commlist.hxx"
 #include "AILocalTraffic.hxx"
 
+
 SG_USING_STD(cout);
 
 // TowerPlaneRec
@@ -50,6 +58,7 @@ TowerPlaneRec::TowerPlaneRec() :
        clearedToLineUp(false),
        clearedToTakeOff(false),
        holdShortReported(false),
+       lineUpReported(false),
        downwindReported(false),
        longFinalReported(false),
        longFinalAcknowledged(false),
@@ -66,6 +75,8 @@ TowerPlaneRec::TowerPlaneRec() :
        opType(TTT_UNKNOWN),
        leg(LEG_UNKNOWN),
        landingType(AIP_LT_UNKNOWN),
+       gearWasUp(false),
+       gearUpReported(false),
        isUser(false)
 {
        plane.callsign = "UNKNOWN";
@@ -77,6 +88,7 @@ TowerPlaneRec::TowerPlaneRec(const PlaneRec& p) :
        clearedToLineUp(false),
        clearedToTakeOff(false),
        holdShortReported(false),
+       lineUpReported(false),
        downwindReported(false),
        longFinalReported(false),
        longFinalAcknowledged(false),
@@ -93,6 +105,8 @@ TowerPlaneRec::TowerPlaneRec(const PlaneRec& p) :
        opType(TTT_UNKNOWN),
        leg(LEG_UNKNOWN),
        landingType(AIP_LT_UNKNOWN),
+       gearWasUp(false),
+       gearUpReported(false),
        isUser(false)
 {
        plane = p;
@@ -104,6 +118,7 @@ TowerPlaneRec::TowerPlaneRec(const Point3D& pt) :
        clearedToLineUp(false),
        clearedToTakeOff(false),
        holdShortReported(false),
+       lineUpReported(false),
        downwindReported(false),
        longFinalReported(false),
        longFinalAcknowledged(false),
@@ -120,6 +135,8 @@ TowerPlaneRec::TowerPlaneRec(const Point3D& pt) :
        opType(TTT_UNKNOWN),
        leg(LEG_UNKNOWN),
        landingType(AIP_LT_UNKNOWN),
+       gearWasUp(false),
+       gearUpReported(false),
        isUser(false)
 {
        plane.callsign = "UNKNOWN";
@@ -132,6 +149,7 @@ TowerPlaneRec::TowerPlaneRec(const PlaneRec& p, const Point3D& pt) :
        clearedToLineUp(false),
        clearedToTakeOff(false),
        holdShortReported(false),
+       lineUpReported(false),
        downwindReported(false),
        longFinalReported(false),
        longFinalAcknowledged(false),
@@ -148,6 +166,8 @@ TowerPlaneRec::TowerPlaneRec(const PlaneRec& p, const Point3D& pt) :
        opType(TTT_UNKNOWN),
        leg(LEG_UNKNOWN),
        landingType(AIP_LT_UNKNOWN),
+       gearWasUp(false),
+       gearUpReported(false),
        isUser(false)
 {
        plane = p;
@@ -288,6 +308,8 @@ 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
@@ -298,7 +320,13 @@ void FGTower::Init() {
        
        // 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,18 +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 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);
        }
 }
 
@@ -446,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) {
@@ -462,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");
        }
 }
 
@@ -483,11 +507,14 @@ void FGTower::Respond() {
                        // 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);
                                }
@@ -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) {
@@ -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,6 +651,15 @@ 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) {
@@ -616,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;
@@ -733,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) {
@@ -855,7 +933,8 @@ 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 {
@@ -1060,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
@@ -1205,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();
@@ -1310,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";
@@ -1319,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 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).
@@ -1345,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
@@ -1460,17 +1584,17 @@ bool FGTower::OnActiveRunway(const 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(const 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
@@ -1482,11 +1606,16 @@ bool FGTower::OnAnyRunway(const 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);
 }
@@ -1684,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.
@@ -2057,6 +2195,25 @@ void FGTower::VFRArrivalContact(const PlaneRec& plane, FGAIPlane* requestee, con
 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(const string& ID) {
        //cout << "Report Final Called at tower " << ident << " by plane " << ID << '\n';
@@ -2297,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();
@@ -2313,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);
@@ -2356,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 ) {
@@ -2379,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 );
@@ -2395,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;
@@ -2434,8 +2626,36 @@ string FGTower::GenText(const string& m, int c) {
                        //break;
                //}
        //}
-       if ( mes[0] ) 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==0)
+               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) {
index 562f08ed541bab5faf2c26f773f5c1fa3b6bf229..0fe4103a7791736f656e228b82fda194b99cee42 100644 (file)
@@ -60,7 +60,8 @@ enum tower_callback_type {
        USER_REPORT_3_MILE_FINAL = 5,
        USER_REPORT_DOWNWIND = 6,
        USER_REPORT_RWY_VACATED = 7,
-       USER_REPORT_GOING_AROUND = 8
+       USER_REPORT_GOING_AROUND = 8,
+       USER_REQUEST_TAKE_OFF = 9
 };
 
 // TODO - need some differentiation of IFR and VFR traffic in order to give the former priority.
@@ -87,6 +88,7 @@ public:
        bool clearedToTakeOff;
        // ought to add time cleared to depart so we can nag if necessary
        bool holdShortReported;
+       bool lineUpReported;
        bool downwindReported;
        bool longFinalReported;
        bool longFinalAcknowledged;
@@ -98,6 +100,8 @@ public:
        bool instructedToGoAround;      // set true if plane told by tower to go around.
        bool onRwy;             // is physically on the runway
        bool nextOnRwy;         // currently projected by tower to be the next on the runway
+       bool gearWasUp;          // Tell to ATC about gear
+       bool gearUpReported;     // Tell to pilot about landing gear
        
        bool vfrArrivalReported;
        bool vfrArrivalAcknowledged;
@@ -139,6 +143,7 @@ public:
        void VFRArrivalContact(const PlaneRec& plane, FGAIPlane* requestee, const LandingType& lt = AIP_LT_UNKNOWN);
        
        void RequestDepartureClearance(const string& ID);
+       void RequestTakeOffClearance(const string& ID);
        void ReportFinal(const string& ID);
        void ReportLongFinal(const string& ID);
        void ReportOuterMarker(const string& ID);
@@ -177,9 +182,11 @@ public:
        bool GetBaseConstraint(double& bpos);
        
        string GenText(const string& m, int c);
+       string GetWeather();
+       string GetATISID();
 
 private:
-       FGATCMgr* ATCmgr;       
+       FGATCMgr* ATCmgr;
        // This is purely for synactic convienience to avoid writing globals->get_ATC_mgr()-> all through the code!
        
        // Respond to a transmission
@@ -213,7 +220,7 @@ private:
        bool OnActiveRunway(const Point3D& pt);
        
        // Figure out if a given position lies on a runway or not
-       bool OnAnyRunway(const Point3D& pt);
+       bool OnAnyRunway(const Point3D& pt, bool onGround);
        
        // Calculate the eta of a plane to the threshold.
        // For ground traffic this is the fastest they can get there.
@@ -255,6 +262,7 @@ private:
        RunwayDetails rwy;      // Assumed to be the active one for now.
        bool rwyOccupied;       // Active runway occupied flag.  For now we'll disregard land-and-hold-short operations.
        FGATCAlignedProjection ortho;   // Orthogonal mapping of the local area with the active runway threshold at the origin
+       FGATCAlignedProjection ortho_temp;      // Ortho for any runway (needed to get plane position in airport)
        
        // Figure out which runways are active.
        // For now we'll just be simple and do one active runway - eventually this will get much more complex
@@ -319,6 +327,8 @@ private:
        
        // Add to vacated list only if not already present
        void AddToVacatedList(TowerPlaneRec* t);
+       
+       void AddToHoldingList(TowerPlaneRec* t);
 
        // Ground can be separate or handled by tower in real life.
        // In the program we will always use a separate FGGround class, but we need to know