]> git.mxchange.org Git - flightgear.git/blobdiff - src/ATC/AILocalTraffic.cxx
Ima Sudonim:
[flightgear.git] / src / ATC / AILocalTraffic.cxx
index 0f71a83693c6f71e7d8e517e7a0143ae58080cfe..fe716d9043b9c7acfb758a068d72266e31a6572e 100644 (file)
@@ -38,6 +38,7 @@ during descent to avoid occasionally landing short or long.
 
 #include <Airports/runways.hxx>
 #include <Main/globals.hxx>
+#include <Main/viewer.hxx>
 #include <Scenery/scenery.hxx>
 #include <Scenery/tilemgr.hxx>
 #include <simgear/math/point3d.hxx>
@@ -106,6 +107,7 @@ FGAILocalTraffic::FGAILocalTraffic() {
        contactTower = false;
        contactGround = false;
        _taxiToGA = false;
+       _removeSelf = false;
        
        descending = false;
        targetDescentRate = 0.0;
@@ -118,6 +120,8 @@ FGAILocalTraffic::FGAILocalTraffic() {
        _savedSlope = 0.0;
        
        _controlled = false;
+       
+       _invisible = false;
 }
 
 FGAILocalTraffic::~FGAILocalTraffic() {
@@ -128,7 +132,7 @@ void FGAILocalTraffic::GetAirportDetails(string id) {
        AirportATC a;
        if(ATC->GetAirportATCDetails(airportID, &a)) {
                if(a.tower_freq) {      // Has a tower - TODO - check the opening hours!!!
-                       tower = (FGTower*)ATC->GetATCPointer(airportID, TOWER); // Maybe need some error checking here
+                       tower = (FGTower*)ATC->GetATCPointer(airportID, TOWER);
                        if(tower == NULL) {
                                // Something has gone wrong - abort or carry on with un-towered operation?
                                SG_LOG(SG_ATC, SG_ALERT, "ERROR - can't get a tower pointer from tower control for " << airportID << " in FGAILocalTraffic::GetAirportDetails() :-(");
@@ -163,27 +167,32 @@ void FGAILocalTraffic::GetAirportDetails(string id) {
 void FGAILocalTraffic::GetRwyDetails(string id) {
        //cout << "GetRwyDetails called" << endl;
        
-       rwy.rwyID = tower->GetActiveRunway();
+       if(_controlled) {
+               rwy.rwyID = tower->GetActiveRunway();
+       } else {
+               // TODO - get a proper runway ID from uncontrolled airports
+               rwy.rwyID = "00";
+       }
        
        // Now we need to get the threshold position and rwy heading
        
        FGRunway runway;
        bool rwyGood = globals->get_runways()->search(id, rwy.rwyID, &runway);
        if(rwyGood) {
-       double hdg = runway.heading;
+       double hdg = runway._heading;
                double other_way = hdg - 180.0;
                while(other_way <= 0.0) {
                        other_way += 360.0;
                }
 
        // move to the +l end/center of the runway
-               //cout << "Runway center is at " << runway.lon << ", " << runway.lat << '\n';
-       Point3D origin = Point3D(runway.lon, runway.lat, aptElev);
+               //cout << "Runway center is at " << runway._lon << ", " << runway._lat << '\n';
+       Point3D origin = Point3D(runway._lon, runway._lat, aptElev);
                Point3D ref = origin;
        double tshlon, tshlat, tshr;
                double tolon, tolat, tor;
-               rwy.length = runway.length * SG_FEET_TO_METER;
-               rwy.width = runway.width * SG_FEET_TO_METER;
+               rwy.length = runway._length * SG_FEET_TO_METER;
+               rwy.width = runway._width * SG_FEET_TO_METER;
        geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), other_way, 
                                rwy.length / 2.0 - 25.0, &tshlat, &tshlon, &tshr );
        geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), hdg, 
@@ -201,7 +210,7 @@ void FGAILocalTraffic::GetRwyDetails(string id) {
                rwy.end1ortho = ortho.ConvertToLocal(rwy.threshold_pos);        // should come out as zero
                rwy.end2ortho = ortho.ConvertToLocal(takeoff_end);
        } else {
-               SG_LOG(SG_ATC, SG_ALERT, "Help  - can't get good runway in FGAILocalTraffic!!\n");
+               SG_LOG(SG_ATC, SG_ALERT, "Help  - can't get good runway at airport " << id << " in FGAILocalTraffic!!");
        }
 }
 
@@ -239,11 +248,15 @@ bool FGAILocalTraffic::Init(const string& callsign, string ICAO, OperatingState
                patternDirection = (rwy.rwyID.substr(2,1) == "R" ? 1 : -1);
        }
        
-       // TODO - this assumes a controlled airport - make sure we revert to CTAF etc if uncontrolled or after-hours.
-       if((initialState == PARKED) || (initialState == TAXIING)) {
-               freq = (double)ground->get_freq() / 100.0;
+       if(_controlled) {
+               if((initialState == PARKED) || (initialState == TAXIING)) {
+                       freq = (double)ground->get_freq() / 100.0;
+               } else {
+                       freq = (double)tower->get_freq() / 100.0;
+               }
        } else {
-               freq = (double)tower->get_freq() / 100.0;
+               freq = 122.8;
+               // TODO - find the proper freq if CTAF or unicom or after-hours.
        }
 
        //cout << "In Init(), initialState = " << initialState << endl;
@@ -279,7 +292,11 @@ bool FGAILocalTraffic::Init(const string& callsign, string ICAO, OperatingState
                // FIXME - implement this case properly
                // For now we'll assume that the plane should start at the hold short in this case
                // and that we're working without ground network elements.  Ie. an airport with no facility file.
-               tuned_station = tower;
+               if(_controlled) {
+                       tuned_station = tower;
+               } else {
+                       tuned_station = NULL;
+               }
                freeTaxi = true;
                // Set a position and orientation in an approximate place for hold short.
                //cout << "rwy.width = " << rwy.width << '\n';
@@ -316,7 +333,11 @@ bool FGAILocalTraffic::Init(const string& callsign, string ICAO, OperatingState
                
                //cout << "Starting in pattern...\n";
                
-               tuned_station = tower;
+               if(_controlled) {
+                       tuned_station = tower;
+               } else {
+                       tuned_station = NULL;
+               }
                
                circuitsToFly = 0;              // ie just fly this circuit and then stop
                touchAndGo = false;
@@ -328,14 +349,16 @@ bool FGAILocalTraffic::Init(const string& callsign, string ICAO, OperatingState
                        leg = DOWNWIND;
                        elevInitGood = false;
                        inAir = true;
-                       track = rwy.hdg - (180 * patternDirection);     //should tend to bring track back into the 0->360 range
+                       SetTrack(rwy.hdg - (180 * patternDirection));
                        slope = 0.0;
                        _pitch = 0.0;
                        _roll = 0.0;
                        IAS = 90.0;
                        descending = false;
                        _aip.setVisible(true);
-                       tower->RegisterAIPlane(plane, this, CIRCUIT, DOWNWIND);
+                       if(_controlled) {
+                               tower->RegisterAIPlane(plane, this, CIRCUIT, DOWNWIND);
+                       }
                        Transform();
                } else {                        
                        // Default to initial position on threshold for now
@@ -383,7 +406,7 @@ void FGAILocalTraffic::DownwindEntry() {
        leg = DOWNWIND;
        elevInitGood = false;
        inAir = true;
-       track = rwy.hdg - (180 * patternDirection);     //should tend to bring track back into the 0->360 range
+       SetTrack(rwy.hdg - (180 * patternDirection));
        slope = 0.0;
        _pitch = 0.0;
        _roll = 0.0;
@@ -399,7 +422,7 @@ void FGAILocalTraffic::StraightInEntry(bool des) {
        leg = FINAL;
        elevInitGood = false;
        inAir = true;
-       track = rwy.hdg;
+       SetTrack(rwy.hdg);
        transmitted = true;     // TODO - fix this hack.
        // TODO - set up the next 5 properly for a descent!
        slope = -5.5;
@@ -448,6 +471,16 @@ void FGAILocalTraffic::FlyCircuits(int numCircuits, bool tag) {
 // Run the internal calculations
 void FGAILocalTraffic::Update(double dt) {
        //cout << "U" << flush;
+       
+       // we shouldn't really need this since there's a LOD of 10K on the whole plane anyway I think.
+       // At the moment though I need to to avoid DList overflows - the whole plane LOD obviously isn't getting picked up.
+       if(!_invisible) {
+               if(dclGetHorizontalSeparation(_pos, Point3D(fgGetDouble("/position/longitude-deg"), fgGetDouble("/position/latitude-deg"), 0.0)) > 8000) _aip.setVisible(false);
+               else _aip.setVisible(true);
+       } else {
+               _aip.setVisible(false);
+       }
+       
        //double responseTime = 10.0;           // seconds - this should get more sophisticated at some point
        responseCounter += dt;
        if((contactTower) && (responseCounter >= 8.0)) {
@@ -489,16 +522,28 @@ void FGAILocalTraffic::Update(double dt) {
                string trns = "GA Parking, Thank you and Good Day";
                //double f = globals->get_ATC_mgr()->GetFrequency(airportID, GROUND) / 100.0;   
                pending_transmission = trns;
-               ConditionalTransmit(5.0);
-               tower->DeregisterAIPlane(plane.callsign);
+               ConditionalTransmit(5.0, 99);
                _taxiToGA = false;
-               // HACK - check if we are at a simple airport or not first
-               globals->get_AI_mgr()->ScheduleRemoval(plane.callsign);
-       }               
+               if(_controlled) {
+                       tower->DeregisterAIPlane(plane.callsign);
+               }
+               // NOTE - we can't delete this instance yet since then the frequency won't get release when the message display finishes.
+       }
+
+       if((_removeSelf) && (responseCounter >= 8.0)) {
+               _removeSelf = false;
+               // MEGA HACK - check if we are at a simple airport or not first instead of simply hardwiring KEMT as the only non-simple airport.
+               // TODO FIXME TODO FIXME !!!!!!!
+               if(airportID != "KEMT") globals->get_AI_mgr()->ScheduleRemoval(plane.callsign);
+       }
        
        if((changeFreq) && (responseCounter > 8.0)) {
                switch(changeFreqType) {
                case TOWER:
+                       if(!tower) {
+                               SG_LOG(SG_ATC, SG_ALERT, "ERROR: Trying to change frequency to tower in FGAILocalTraffic, but tower is NULL!!!");
+                               break;
+                       }
                        tuned_station = tower;
                        freq = (double)tower->get_freq() / 100.0;
                        //Transmit("DING!");
@@ -516,12 +561,17 @@ void FGAILocalTraffic::Update(double dt) {
                        Transmit(2);
                        break;
                case GROUND:
+                       if(!tower) {
+                               SG_LOG(SG_ATC, SG_ALERT, "ERROR: Trying to change frequency to ground in FGAILocalTraffic, but tower is NULL!!!");
+                               break;
+                       }
+                       if(!ground) {
+                               SG_LOG(SG_ATC, SG_ALERT, "ERROR: Trying to change frequency to ground in FGAILocalTraffic, but ground is NULL!!!");
+                               break;
+                       }
                        tower->DeregisterAIPlane(plane.callsign);
                        tuned_station = ground;
                        freq = (double)ground->get_freq() / 100.0;
-                       // HACK - check if we are at a simple airport or not first
-                       // TODO FIXME TODO FIXME !!!!!!!
-                       if(airportID != "KEMT") globals->get_AI_mgr()->ScheduleRemoval(plane.callsign);
                        break;
                // And to avoid compiler warnings...
                case APPROACH:  break;
@@ -665,8 +715,13 @@ void FGAILocalTraffic::Update(double dt) {
                                // Do some communication
                                // airport name + tower + airplane callsign + location + request taxi for + operation type + ?
                                string trns = "";
-                               trns += tower->get_name();
-                               trns += " tower ";
+                               if(_controlled) {
+                                       trns += tower->get_name();
+                                       trns += " tower ";
+                               } else {
+                                       trns += "Traffic ";
+                                       // TODO - get the airport name somehow if uncontrolled
+                               }
                                trns += plane.callsign;
                                trns += " on apron parking request taxi for traffic pattern";
                                //cout << "trns = " << trns << endl;
@@ -702,7 +757,7 @@ void FGAILocalTraffic::Update(double dt) {
        //fgSetDouble("/AI/Local1/ortho-y", (ortho.ConvertToLocal(_pos)).y());
        //fgSetDouble("/AI/Local1/elev", _pos.elev() * SG_METER_TO_FEET);
        
-       // And finally, call parent for transmission rendering
+       // And finally, call parent.
        FGAIPlane::Update(dt);
 }
 
@@ -800,6 +855,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                IAS = vel + (cos((_hdg - wind_from) * DCL_DEGREES_TO_RADIANS) * wind_speed);
                if(IAS >= 70) {
                        leg = CLIMBOUT;
+                       SetTrack(rwy.hdg);      // Hands over control of turning to AIPlane
                        _pitch = 10.0;
                        IAS = best_rate_of_climb_speed;
                        //slope = 7.0;  
@@ -808,7 +864,6 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                }
                break;
        case CLIMBOUT:
-               track = rwy.hdg;
                // Turn to crosswind if above 700ft AND if other traffic allows
                // (decided in FGTower and accessed through GetCrosswindConstraint(...)).
                // According to AIM, traffic should climb to within 300ft of pattern altitude before commencing crosswind turn.
@@ -843,16 +898,13 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                }               
                break;
        case TURN1:
-               track += (360.0 / turn_time) * dt * patternDirection;
-               Bank(25.0 * patternDirection);
+               SetTrack(rwy.hdg + (90.0 * patternDirection));
                if((track < (rwy.hdg - 89.0)) || (track > (rwy.hdg + 89.0))) {
                        leg = CROSSWIND;
                }
                break;
        case CROSSWIND:
                goAround = false;
-               LevelWings();
-               track = rwy.hdg + (90.0 * patternDirection);
                if((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) {
                        slope = 0.0;
                        _pitch = 0.0;
@@ -873,8 +925,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                }
                break;
        case TURN2:
-               track += (360.0 / turn_time) * dt * patternDirection;
-               Bank(25.0 * patternDirection);
+               SetTrack(rwy.hdg - (180 * patternDirection));
                // just in case we didn't make height on crosswind
                if((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) {
                        slope = 0.0;
@@ -884,12 +935,9 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                if((track < (rwy.hdg - 179.0)) || (track > (rwy.hdg + 179.0))) {
                        leg = DOWNWIND;
                        transmitted = false;
-                       //roll = 0.0;
                }
                break;
        case DOWNWIND:
-               LevelWings();
-               track = rwy.hdg - (180 * patternDirection);     //should tend to bring track back into the 0->360 range
                // just in case we didn't make height on crosswind
                if(((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 995) && ((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET < 1015)) {
                        slope = 0.0;
@@ -947,14 +995,12 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                }
                break;
        case TURN3:
-               track += (360.0 / turn_time) * dt * patternDirection;
-               Bank(25.0 * patternDirection);
+               SetTrack(rwy.hdg - (90 * patternDirection));
                if(fabs(rwy.hdg - track) < 91.0) {
                        leg = BASE;
                }
                break;
        case BASE:
-               LevelWings();
                if(!transmitted) {
                        // Base report should only be transmitted at uncontrolled airport - not towered.
                        if(!_controlled) TransmitPatternPositionReport();
@@ -977,8 +1023,6 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                        IAS = 70.0;
                }
                
-               track = rwy.hdg - (90 * patternDirection);
-
                // Try and arrange to turn nicely onto final
                turn_circumference = IAS * 0.514444 * turn_time;        
                //Hmmm - this is an interesting one - ground vs airspeed in relation to turn radius
@@ -991,8 +1035,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                }
                break;
        case TURN4:
-               track += (360.0 / turn_time) * dt * patternDirection;
-               Bank(25.0 * patternDirection);
+               SetTrack(rwy.hdg);
                if(fabs(track - rwy.hdg) < 0.6) {
                        leg = FINAL;
                        vel = nominal_final_speed;
@@ -1062,7 +1105,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                        }
                }
                // Try and track the extended centreline
-               track = rwy.hdg - (0.2 * orthopos.x());
+               SetTrack(rwy.hdg - (0.2 * orthopos.x()));
                //cout << "orthopos.x() = " << orthopos.x() << " hdg = " << hdg << '\n';
                if(_pos.elev() < (rwy.threshold_pos.elev()+20.0+wheelOffset)) {
                        DoGroundElev(); // Need to call it here expicitly on final since it's only called
@@ -1077,6 +1120,8 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                                        _pitch = 0.0;
                                        leg = LANDING_ROLL;
                                        inAir = false;
+                                       LevelWings();
+                                       ClearTrack();   // Take over explicit track handling since AIPlane currently always banks when changing course 
                                }
                        }       // else need a fallback position based on arpt elev in case ground elev determination fails?
                } else {
@@ -1262,6 +1307,10 @@ void FGAILocalTraffic::ProcessCallback(int code) {
                tower->ReportDownwind(plane.callsign);
        } else if(code == 13) {
                tower->ReportFinal(plane.callsign);
+       } else if(code == 99) { // Flag this instance for deletion
+               responseCounter = 0;
+               _removeSelf = true;
+               SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " delete instance callback called.");
        }
 }
 
@@ -1331,9 +1380,10 @@ void FGAILocalTraffic::ExitRunway(Point3D orthopos) {
        } else {
                // Something must have gone wrong with the ground network file - or there is only a rwy here and no exits defined
                SG_LOG(SG_ATC, SG_INFO, "No exits found by FGAILocalTraffic from runway " << rwy.rwyID << " at " << airportID << '\n');
-               //cout << "No exits found by " << plane.callsign << " from runway " << rwy.rwyID << " at " << airportID << '\n';
+               //if(airportID == "KRHV") cout << "No exits found by " << plane.callsign << " from runway " << rwy.rwyID << " at " << airportID << '\n';
                // What shall we do - just remove the plane from sight?
                _aip.setVisible(false);
+               _invisible = true;
                //cout << "Setting visible false\n";
                //tower->ReportRunwayVacated(plane.callsign);
                string trns = "Clear of the runway ";
@@ -1517,30 +1567,32 @@ void FGAILocalTraffic::Taxi(double dt) {
 // Either this function or the logic of how often it is called
 // will almost certainly change.
 void FGAILocalTraffic::DoGroundElev() {
-
        // It would be nice if we could set the correct tile center here in order to get a correct
        // answer with one call to the function, but what I tried in the two commented-out lines
        // below only intermittently worked, and I haven't quite groked why yet.
        //SGBucket buck(pos.lon(), pos.lat());
        //aip.getSGLocation()->set_tile_center(Point3D(buck.get_center_lon(), buck.get_center_lat(), 0.0));
        
+       // Only do the proper hitlist stuff if we are within visible range of the viewer.
        double visibility_meters = fgGetDouble("/environment/visibility-m");
-       //globals->get_tile_mgr()->prep_ssg_nodes( acmodel_location,
-       globals->get_tile_mgr()->prep_ssg_nodes( _aip.getSGLocation(),  visibility_meters );
-        Point3D scenery_center = globals->get_scenery()->get_center();
-       globals->get_tile_mgr()->update( _aip.getSGLocation(), visibility_meters, (_aip.getSGLocation())->get_absolute_view_pos( scenery_center ) );
-       // save results of update in SGLocation for fdm...
-       
-       //if ( globals->get_scenery()->get_cur_elev() > -9990 ) {
-       //      acmodel_location->
-       //      set_cur_elev_m( globals->get_scenery()->get_cur_elev() );
-       //}
-       
-       // The need for this here means that at least 2 consecutive passes are needed :-(
-       _aip.getSGLocation()->set_tile_center( globals->get_scenery()->get_next_center() );
-       
-       //cout << "Transform Elev is " << globals->get_scenery()->get_cur_elev() << '\n';
-       _aip.getSGLocation()->set_cur_elev_m(globals->get_scenery()->get_cur_elev());
-       //return(globals->get_scenery()->get_cur_elev());
+       FGViewer* vw = globals->get_current_view();
+       if(dclGetHorizontalSeparation(_pos, Point3D(vw->getLongitude_deg(), vw->getLatitude_deg(), 0.0)) > visibility_meters) {
+               _aip.getSGLocation()->set_cur_elev_m(aptElev);
+               return;
+       }
+
+        // FIXME: make shure the pos.lat/pos.lon values are in degrees ...
+        double range = 500.0;
+        double lat = _aip.getSGLocation()->getLatitude_deg();
+        double lon = _aip.getSGLocation()->getLongitude_deg();
+        if (!globals->get_tile_mgr()->scenery_available(lat, lon, range)) {
+          // Try to shedule tiles for that position.
+          globals->get_tile_mgr()->update( _aip.getSGLocation(), range );
+        }
+
+        // FIXME: make shure the pos.lat/pos.lon values are in degrees ...
+        double alt;
+        if (globals->get_scenery()->get_elevation_m(lat, lon, 20000.0, alt))
+          _aip.getSGLocation()->set_cur_elev_m(alt);
 }