]> git.mxchange.org Git - flightgear.git/blobdiff - src/ATC/AILocalTraffic.cxx
Fix the nmea and garmin output to a) fake a GSA sentence, b) fix a y2k bug
[flightgear.git] / src / ATC / AILocalTraffic.cxx
index 74293377ba286440908b627627c36f9dd129f204..e427bb382cd5c8fa62f3943a91a3938f1f7a6809 100644 (file)
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
+/*==========================================================
+
+TODO list.
+
+Should get pattern direction from tower.
+
+Need to continually monitor and adjust deviation from glideslope
+during descent to avoid occasionally landing short or long.
+
+============================================================*/
+
 #ifdef HAVE_CONFIG_H
 #  include <config.h>
 #endif
@@ -85,6 +96,8 @@ FGAILocalTraffic::FGAILocalTraffic() {
        
        descending = false;
        targetDescentRate = 0.0;
+       goAround = false;
+       goAroundCalled = false;
 }
 
 FGAILocalTraffic::~FGAILocalTraffic() {
@@ -149,7 +162,7 @@ The second is that the user has started the sim at or close to the airport, and
 hence the traffic must be initialised with respect to the user as well as each other.
 To a certain extent it's FGAIMgr that has to worry about this, but we need to provide
 sufficient initialisation functionality within the plane classes to allow the manager
-to initialy position them where and how required.
+to initially position them where and how required.
 */
 bool FGAILocalTraffic::Init(string ICAO, OperatingState initialState, PatternLeg initialLeg) {
        //cout << "FGAILocalTraffic.Init(...) called" << endl;
@@ -168,7 +181,7 @@ bool FGAILocalTraffic::Init(string ICAO, OperatingState initialState, PatternLeg
        AirportATC a;
        if(ATC->GetAirportATCDetails(airportID, &a)) {
                if(a.tower_freq) {      // Has a tower
-                       tower = (FGTower*)ATC->GetATCPointer((string)airportID, TOWER); // Maybe need some error checking here
+                       tower = (FGTower*)ATC->GetATCPointer(airportID, TOWER); // Maybe need some error checking here
                        if(tower == NULL) {
                                // Something has gone wrong - abort or carry on with un-towered operation?
                                return(false);
@@ -189,6 +202,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 +249,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;
+               // 18/10/03 - adding the ability to start on downwind (mainly to speed testing of the go-around code!!)
                
-               // 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;
-               
-               // 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 +263,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();
@@ -281,6 +315,7 @@ bool FGAILocalTraffic::Init(string ICAO, OperatingState initialState, PatternLeg
 
 // Return what type of landing we're doing on this circuit
 LandingType FGAILocalTraffic::GetLandingOption() {
+       //cout << "circuitsToFly = " << circuitsToFly << '\n';
        if(circuitsToFly) {
                return(touchAndGo ? TOUCH_AND_GO : STOP_AND_GO);
        } else {
@@ -339,7 +374,17 @@ void FGAILocalTraffic::Update(double dt) {
                        //Transmit("DING!");
                        // Contact the tower, even if only virtually
                        changeFreq = false;
-                       tower->ContactAtHoldShort(plane, this, CIRCUIT);
+                       pending_transmission = plane.callsign;
+                       pending_transmission += " at hold short for runway ";
+                       pending_transmission += ConvertRwyNumToSpokenString(rwy.rwyID);
+                       pending_transmission += " traffic pattern ";
+                       if(circuitsToFly) {
+                               pending_transmission += ConvertNumToSpokenDigits(circuitsToFly + 1);
+                               pending_transmission += " circuits touch and go";
+                       } else {
+                               pending_transmission += " one circuit to full stop";
+                       }
+                       Transmit(2);
                        break;
                case GROUND:
                        tuned_station = ground;
@@ -359,15 +404,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);
@@ -440,7 +487,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
@@ -533,6 +580,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;
        default:
                break;
        }
@@ -596,16 +648,19 @@ 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) {
                                        cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n'; 
                                        leg = TURN1;
                                }
-                       } else {
+                       } 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;
                        }
@@ -617,6 +672,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;
@@ -626,6 +689,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) {
@@ -767,6 +831,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();
@@ -944,13 +1017,13 @@ void FGAILocalTraffic::TransmitPatternPositionReport(void) {
                // Fall through to FINAL
        case FINAL:             // maybe this should include long/short final if appropriate?
                trns += "final ";
+               code = 13;
                break;
        default:                // Hopefully this won't be used
                trns += "pattern ";
                break;
        }
-       // FIXME - I've hardwired the runway call as well!! (We could work this out from rwy heading and mag deviation)
-       trns += ConvertRwyNumToSpokenString(1);
+       trns += ConvertRwyNumToSpokenString(rwy.rwyID);
        
        // And add the airport name again
        trns += tower->get_name();
@@ -960,16 +1033,22 @@ void FGAILocalTraffic::TransmitPatternPositionReport(void) {
 }
 
 // Callback handler
+// TODO - Really should enumerate these coded values.
 void FGAILocalTraffic::ProcessCallback(int code) {
        // 1 - Request Departure from ground
+       // 2 - Report at hold short
        // 10 - report crosswind
        // 11 - report downwind
        // 12 - report base
        // 13 - report final
        if(code == 1) {
                ground->RequestDeparture(plane, this);
+       } else if(code == 2) {
+               tower->ContactAtHoldShort(plane, this, CIRCUIT);
        } else if(code == 11) {
                tower->ReportDownwind(plane.callsign);
+       } else if(code == 13) {
+               tower->ReportFinal(plane.callsign);
        }
 }