]> git.mxchange.org Git - flightgear.git/commitdiff
AI plane should go around instead of landing on user if user dawdles on runway now
authordaveluff <daveluff>
Sun, 19 Oct 2003 20:38:08 +0000 (20:38 +0000)
committerdaveluff <daveluff>
Sun, 19 Oct 2003 20:38:08 +0000 (20:38 +0000)
src/ATC/AILocalTraffic.cxx
src/ATC/AILocalTraffic.hxx
src/ATC/tower.cxx
src/ATC/tower.hxx

index 81c2cfa217ab58d6c21130bad5186311685b8abc..9c45bc1dc6402c899500cd4f491204caf057a1da 100644 (file)
@@ -85,6 +85,8 @@ FGAILocalTraffic::FGAILocalTraffic() {
        
        descending = false;
        targetDescentRate = 0.0;
+       goAround = false;
+       goAroundCalled = false;
 }
 
 FGAILocalTraffic::~FGAILocalTraffic() {
@@ -189,6 +191,9 @@ bool FGAILocalTraffic::Init(string ICAO, OperatingState initialState, PatternLeg
        } else {
                //cout << "Unable to find airport details in FGAILocalTraffic::Init()\n";
        }
+       
+               // Get the active runway details (and copy them into rwy)
+               GetRwyDetails();
 
        // Get the airport elevation
        aptElev = dclGetAirportElev(airportID.c_str()) * SG_FEET_TO_METER;
@@ -233,28 +238,11 @@ bool FGAILocalTraffic::Init(string ICAO, OperatingState initialState, PatternLeg
                // since we've got the implementation for this case already.
                // TODO - implement proper generic in_pattern startup.
                
-               tuned_station = tower;
-               
-               // Get the active runway details (and copy them into rwy)
-               GetRwyDetails();
-
-               // Initial position on threshold for now
-               pos.setlat(rwy.threshold_pos.lat());
-               pos.setlon(rwy.threshold_pos.lon());
-               pos.setelev(rwy.threshold_pos.elev());
-               hdg = rwy.hdg;
+               // 18/10/03 - adding the ability to start on downwind (mainly to speed testing of the go-around code!!)
                
-               // Now we've set the position we can do the ground elev
-               // This might not always be necessary if we implement in-air start
-               elevInitGood = false;
-               inAir = false;
-               DoGroundElev();
+               //cout << "Starting in pattern...\n";
                
-               pitch = 0.0;
-               roll = 0.0;
-               leg = TAKEOFF_ROLL;
-               vel = 0.0;
-               slope = 0.0;
+               tuned_station = tower;
                
                circuitsToFly = 0;              // ie just fly this circuit and then stop
                touchAndGo = false;
@@ -264,7 +252,42 @@ bool FGAILocalTraffic::Init(string ICAO, OperatingState initialState, PatternLeg
                if(rwy.rwyID.size() == 3) {
                        patternDirection = (rwy.rwyID.substr(2,1) == "R" ? 1 : -1);
                }
-               
+
+               if(initialLeg == DOWNWIND) {
+                       pos = ortho.ConvertFromLocal(Point3D(1000*patternDirection, 800, 0.0));
+                       pos.setelev(rwy.threshold_pos.elev() + 1000 * SG_FEET_TO_METER);
+                       hdg = rwy.hdg + 180.0;
+                       leg = DOWNWIND;
+                       elevInitGood = false;
+                       inAir = true;
+                       track = rwy.hdg - (180 * patternDirection);     //should tend to bring track back into the 0->360 range
+                       slope = 0.0;
+                       pitch = 0.0;
+                       roll = 0.0;
+                       IAS = 90.0;
+                       descending = false;
+                       aip.setVisible(true);
+                       tower->RegisterAIPlane(plane, this, CIRCUIT, DOWNWIND);
+               } else {                        
+                       // Default to initial position on threshold for now
+                       pos.setlat(rwy.threshold_pos.lat());
+                       pos.setlon(rwy.threshold_pos.lon());
+                       pos.setelev(rwy.threshold_pos.elev());
+                       hdg = rwy.hdg;
+                       
+                       // Now we've set the position we can do the ground elev
+                       // This might not always be necessary if we implement in-air start
+                       elevInitGood = false;
+                       inAir = false;
+                       DoGroundElev();
+                       
+                       pitch = 0.0;
+                       roll = 0.0;
+                       leg = TAKEOFF_ROLL;
+                       vel = 0.0;
+                       slope = 0.0;
+               }
+       
                operatingState = IN_PATTERN;
                
                Transform();
@@ -370,15 +393,17 @@ void FGAILocalTraffic::Update(double dt) {
        switch(operatingState) {
        case IN_PATTERN:
                //cout << "In IN_PATTERN\n";
-               if(!inAir) DoGroundElev();
-               if(!elevInitGood) {
-                       if(aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
-                               pos.setelev(aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
-                               //cout << "TAKEOFF_ROLL, POS = " << pos.lon() << ", " << pos.lat() << ", " << pos.elev() << '\n';
-                               //Transform();
-                               aip.setVisible(true);
-                               //cout << "Making plane visible!\n";
-                               elevInitGood = true;
+               if(!inAir) {
+                       DoGroundElev();
+                       if(!elevInitGood) {
+                               if(aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
+                                       pos.setelev(aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
+                                       //cout << "TAKEOFF_ROLL, POS = " << pos.lon() << ", " << pos.lat() << ", " << pos.elev() << '\n';
+                                       //Transform();
+                                       aip.setVisible(true);
+                                       //cout << "Making plane visible!\n";
+                                       elevInitGood = true;
+                               }
                        }
                }
                FlyTrafficPattern(dt);
@@ -451,7 +476,7 @@ void FGAILocalTraffic::Update(double dt) {
                if(circuitsToFly) {
                        if((taxiRequestPending) && (taxiRequestCleared)) {
                                //cout << "&" << flush;
-                               // Get the active runway details (and copy them into rwy)
+                               // Get the active runway details (in case they've changed since init)
                                GetRwyDetails();
                                
                                // Get the takeoff node for the active runway, get a path to it and start taxiing
@@ -544,11 +569,11 @@ void FGAILocalTraffic::RegisterTransmission(int code) {
                clearedToTakeOff = true;
                SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " cleared to take-off...");
                break;
-//     case 13: // Go around!
-//             responseCounter = 0;
-//             goAround = true;
-//             SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " told to go-around!!");
-//             break;
+       case 13: // Go around!
+               responseCounter = 0;
+               goAround = true;
+               SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " told to go-around!!");
+               break;
        default:
                break;
        }
@@ -612,9 +637,11 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                break;
        case CLIMBOUT:
                track = rwy.hdg;
-               // Turn to crosswind if above 600ft AND if other traffic allows
+               // Turn to crosswind if above 700ft AND if other traffic allows
                // (decided in FGTower and accessed through GetCrosswindConstraint(...)).
-               if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 600) {
+               // According to AIM, traffic should climb to within 300ft of pattern altitude before commencing crosswind turn.
+               // TODO - At hot 'n high airports this may be 500ft AGL though - need to make this a variable.
+               if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 700) {
                        double cc = 0.0;
                        if(tower->GetCrosswindConstraint(cc)) {
                                if(orthopos.y() > cc) {
@@ -622,6 +649,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                                        leg = TURN1;
                                }
                        } else if(orthopos.y() > 1500.0) {   // Added this constraint as a hack to prevent turning too early when going around.
+                               // TODO - We should be doing it as a distance from takeoff end, not theshold end though.
                                cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n'; 
                                leg = TURN1;
                        }
@@ -633,6 +661,14 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                        pitch = 0.0;
                        IAS = 80.0;             // FIXME - use smooth transistion to new speed and attitude.
                }
+               if(goAround && !goAroundCalled) {
+                       if(responseCounter > 5.5) {
+                               pending_transmission = plane.callsign;
+                               pending_transmission += " going around";
+                               Transmit();
+                               goAroundCalled = true;
+                       }
+               }               
                break;
        case TURN1:
                track += (360.0 / turn_time) * dt * patternDirection;
@@ -642,6 +678,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                }
                break;
        case CROSSWIND:
+               goAround = false;
                LevelWings();
                track = rwy.hdg + (90.0 * patternDirection);
                if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) {
@@ -783,6 +820,15 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                }
                break;
        case FINAL:
+               if(goAround && responseCounter > 2.0) {
+                       leg = CLIMBOUT;
+                       pitch = 8.0;
+                       IAS = best_rate_of_climb_speed;
+                       slope = 5.0;    // A bit less steep than the initial climbout.
+                       inAir = true;
+                       goAroundCalled = false;
+                       break;
+               }
                LevelWings();
                if(!transmitted) {
                        TransmitPatternPositionReport();
index 7df7fb60f2501a1bf29a0d050b4e5846b7d0fa4f..d6f21c73592fdd8446b5ec0a4fad9ed7c285645d 100644 (file)
@@ -176,6 +176,8 @@ private:
        bool clearedToLineUp;
        bool clearedToTakeOff;
        bool liningUp;  // Set true when the turn onto the runway heading is commenced when taxiing out
+       bool goAround;  // Set true if need to go-around
+       bool goAroundCalled;    // Set true during go-around only after we have called our go-around on the radio
        bool contactTower;      // we have been told to contact tower
        bool contactGround;     // we have been told to contact ground
        bool changeFreq;        // true when we need to change frequency
index 11cb741c26751166249b0138a3323559b96a740e..acef4984ea7ff3a61f9d97857f211c58f2567ca4 100644 (file)
@@ -44,6 +44,7 @@ longFinalReported(false),
 longFinalAcknowledged(false),
 finalReported(false),
 finalAcknowledged(false),
+instructedToGoAround(false),
 onRwy(false),
 nextOnRwy(false),
 opType(TTT_UNKNOWN),
@@ -64,6 +65,7 @@ longFinalReported(false),
 longFinalAcknowledged(false),
 finalReported(false),
 finalAcknowledged(false),
+instructedToGoAround(false),
 onRwy(false),
 nextOnRwy(false),
 opType(TTT_UNKNOWN),
@@ -84,6 +86,7 @@ longFinalReported(false),
 longFinalAcknowledged(false),
 finalReported(false),
 finalAcknowledged(false),
+instructedToGoAround(false),
 onRwy(false),
 nextOnRwy(false),
 opType(TTT_UNKNOWN),
@@ -105,6 +108,7 @@ longFinalReported(false),
 longFinalAcknowledged(false),
 finalReported(false),
 finalAcknowledged(false),
+instructedToGoAround(false),
 onRwy(false),
 nextOnRwy(false),
 opType(TTT_UNKNOWN),
@@ -273,6 +277,7 @@ void FGTower::Update(double dt) {
                //      cout << "  dt = " << dt << "  timeSinceLastDeparture = " << timeSinceLastDeparture << '\n';
        }
        
+       //cout << ident << " respond = " << respond << " responseReqd = " << responseReqd << '\n'; 
        if(respond) {
                if(!responseReqd) SG_LOG(SG_ATC, SG_ALERT, "ERROR - respond is true and responseReqd is false in FGTower::Update(...)");
                Respond();
@@ -411,6 +416,7 @@ void FGTower::Respond() {
                        }
                        t->holdShortReported = false;
                } else if(t->finalReported && !(t->finalAcknowledged)) {
+                       bool disp = true;
                        string trns = t->plane.callsign;
                        if(t->nextOnRwy && !rwyOccupied) {
                                if(t->landingType == FULL_STOP) {
@@ -420,11 +426,15 @@ void FGTower::Respond() {
                                }
                                // TODO - add winds
                                t->clearedToLand = true;
+                       } else if(t->eta < 20) {
+                               // Do nothing - we'll be telling it to go around in less than 10 seconds if the
+                               // runway doesn't clear so no point in calling "continue approach".
+                               disp = false;
                        } else {
                                trns += " continue approach";
                                t->clearedToLand = false;
                        }
-                       if(display) {
+                       if(display && disp) {
                                globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
                        }
                        t->finalAcknowledged = true;
@@ -714,6 +724,7 @@ void FGTower::CheckCircuitList(double dt) {
                        case CROSSWIND:
                                crosswind_leg_pos = tortho.y();
                                //cout << "crosswind_leg_pos = " << crosswind_leg_pos << '\n';
+                               t->instructedToGoAround = false;
                                break;
                        case TURN1:
                                // Fall through to climbout
@@ -735,6 +746,22 @@ void FGTower::CheckCircuitList(double dt) {
                
                if(t->leg == FINAL) {
                        if(t->landingType == FULL_STOP) t->opType = INBOUND;
+                       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";
+                               if(display) {
+                                       globals->get_ATC_display()->RegisterSingleMessage(trns, 0);
+                               }
+                               t->instructedToGoAround = true;
+                               if(t->planePtr) {
+                                       cout << "Registering Go-around transmission with AI plane\n";
+                                       t->planePtr->RegisterTransmission(13);
+                               }
+                       }
                } else if(t->leg == LANDING_ROLL) {
                        rwyList.push_front(t);
                        // TODO - if(!clearedToLand) shout something!!
@@ -1053,6 +1080,37 @@ bool FGTower::AddToTrafficList(TowerPlaneRec* t, bool holding) {
        return(holding ? firstTime : conflict);
 }
 
+// Add a tower plane rec with ETA to the circuit list in the correct position ETA-wise
+// Returns true if this might cause a separation conflict (based on ETA) with other traffic, false otherwise.
+bool FGTower::AddToCircuitList(TowerPlaneRec* t) {
+       //cout << "ADD: " << circuitList.size();
+       //cout << "AddToCircuitList called, currently size = " << circuitList.size() << endl;
+       double separation_time = 60.0;  // seconds - this is currently a guess for light plane separation, and includes a few seconds for a holding plane to taxi onto the rwy.
+       bool conflict = false;
+       tower_plane_rec_list_iterator twrItr;
+       for(twrItr = circuitList.begin(); twrItr != circuitList.end(); twrItr++) {
+                       TowerPlaneRec* tpr = *twrItr;
+                               
+                               if(t->eta < tpr->eta) {
+                                       // Ugg - this one's tricky.
+                                       // 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);
+                                       } 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);
+                                       }
+                                       //cout << "\tC\t" << circuitList.size() << '\n';
+                                       return(conflict);
+                               }
+       }
+       // If we get here we must be at the end of the list, or maybe the list is empty.
+       circuitList.push_back(t);       // TODO - check the separation with the preceding plane for the conflict flag.
+       //cout << "\tE\t" << circuitList.size() << endl;
+       return(conflict);
+}
+
 
 // Calculate the eta of a plane to the threshold.
 // For ground traffic this is the fastest they can get there.
@@ -1267,8 +1325,10 @@ void FGTower::ContactAtHoldShort(PlaneRec plane, FGAIPlane* requestee, tower_tra
        t->clearedToLineUp = false;
        t->clearedToTakeOff = false;
        t->opType = operation;
+       t->pos = requestee->GetPos();
        
        //cout << "Hold Short reported by " << plane.callsign << '\n';
+       SG_LOG(SG_ATC, SG_BULK, "Hold Short reported by " << plane.callsign);
 
 /*     
        bool next = AddToTrafficList(t, true);
@@ -1288,6 +1348,28 @@ void FGTower::ContactAtHoldShort(PlaneRec plane, FGAIPlane* requestee, tower_tra
        responseReqd = true;
 }
 
+// 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) {
+       // At the moment this is only going to be tested with inserting an AI plane on downwind
+       TowerPlaneRec* t = new TowerPlaneRec;
+       t->plane = plane;
+       t->planePtr = ai;
+       t->opType = op;
+       t->leg = lg;
+       t->pos = ai->GetPos();
+       
+       CalcETA(t);
+       
+       if(op == CIRCUIT && lg != LEG_UNKNOWN) {
+               AddToCircuitList(t);
+       } else {
+               // FLAG A WARNING
+       }
+       
+       doThresholdUseOrder();
+}
+
 void FGTower::RequestLandingClearance(string ID) {
        //cout << "Request Landing Clearance called...\n";
        
index 9c57c61fceb9519b9413a37dd8771aa006c4de61..21095126be75747c42bbd21c1059b10945039aa6 100644 (file)
@@ -82,6 +82,7 @@ public:
        bool longFinalAcknowledged;
        bool finalReported;
        bool finalAcknowledged;
+       bool instructedToGoAround;      // set true if told by tower to go around
        bool onRwy;             // is physically on the runway
        bool nextOnRwy;         // currently projected by tower to be the next on the runway
 
@@ -128,6 +129,10 @@ public:
        // Contact tower when at a hold short for departure - for now we'll assume plane - maybe vehicles might want to cross runway eventually?
        void ContactAtHoldShort(PlaneRec plane, FGAIPlane* requestee, tower_traffic_type operation);
        
+       // 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 RegisterAIPlane(PlaneRec plane, FGAIPlane* ai, tower_traffic_type op, PatternLeg lg = LEG_UNKNOWN);
+       
        // Public interface to the active runway - this will get more complex 
        // in the future and consider multi-runway use, airplane weight etc.
        inline string GetActiveRunway() { return activeRwy; }
@@ -272,6 +277,8 @@ private:
        // Add a tower plane rec with ETA to the traffic list in the correct position ETA-wise.
        // Returns true if this could cause a threshold ETA conflict with other traffic, false otherwise.
        bool AddToTrafficList(TowerPlaneRec* t, bool holding = false);
+       
+       bool AddToCircuitList(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