]> git.mxchange.org Git - flightgear.git/blobdiff - src/ATCDCL/AILocalTraffic.cxx
Fix two bugs in the new autopilot code
[flightgear.git] / src / ATCDCL / AILocalTraffic.cxx
index 2156fbe13b249cd711ceb9bbb242f8a8b8cf13ed..8a66eda6e0a976cc67e1ac5c96c86599bc9b887c 100644 (file)
@@ -34,15 +34,12 @@ during descent to avoid occasionally landing short or long.
 #  include <config.h>
 #endif
 
-#include <simgear/scene/model/location.hxx>
-
 #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>
-#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/math/SGMath.hxx>
 #include <simgear/misc/sg_path.hxx>
 #include <string>
 #include <math.h>
@@ -55,16 +52,6 @@ using std::string;
 #include "AIMgr.hxx"
 
 FGAILocalTraffic::FGAILocalTraffic() {
-       /*ssgBranch *model = sgLoad3DModel( globals->get_fg_root(),
-                                         planepath.c_str(),
-                                         globals->get_props(),
-                                         globals->get_sim_time_sec() );
-       *//*
-       _model = model;
-       _aip.init(_model);
-       */
-       //SetModel(model);
-       
        ATC = globals->get_ATC_mgr();
        
        // TODO - unhardwire this
@@ -165,20 +152,11 @@ void FGAILocalTraffic::GetAirportDetails(const string& id) {
 void FGAILocalTraffic::GetRwyDetails(const string& id) {
        //cout << "GetRwyDetails called" << endl;
        
-       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
-       
   const FGAirport* apt = fgFindAirportID(id);
   assert(apt);
-  FGRunway runway(apt->getRunwayByIdent(rwy.rwyID));
+  FGRunway* runway(apt->getActiveRunwayForUsage());
 
-  double hdg = runway._heading;
+  double hdg = runway->headingDeg();
   double other_way = hdg - 180.0;
   while(other_way <= 0.0) {
     other_way += 360.0;
@@ -186,20 +164,18 @@ void FGAILocalTraffic::GetRwyDetails(const string& id) {
   
        // 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);
-               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;
-       geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), other_way, 
+       double tshlon = 0.0, tshlat = 0.0, tshr;
+               double tolon = 0.0, tolat = 0.0, tor;
+               rwy.length = runway->lengthM();
+               rwy.width = runway->widthM();
+       geo_direct_wgs_84 ( aptElev, runway->latitude(), runway->longitude(), other_way, 
                                rwy.length / 2.0 - 25.0, &tshlat, &tshlon, &tshr );
-       geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), hdg, 
+       geo_direct_wgs_84 ( aptElev, runway->latitude(), runway->longitude(), hdg, 
                                rwy.length / 2.0 - 25.0, &tolat, &tolon, &tor );
                // Note - 25 meters in from the runway end is a bit of a hack to put the plane ahead of the user.
                // now copy what we need out of runway into rwy
-       rwy.threshold_pos = Point3D(tshlon, tshlat, aptElev);
-               Point3D takeoff_end = Point3D(tolon, tolat, aptElev);
+       rwy.threshold_pos = SGGeod::fromDegM(tshlon, tshlat, aptElev);
+               SGGeod takeoff_end = SGGeod::fromDegM(tolon, tolat, aptElev);
                //cout << "Threshold position = " << tshlon << ", " << tshlat << ", " << aptElev << '\n';
                //cout << "Takeoff position = " << tolon << ", " << tolat << ", " << aptElev << '\n';
                rwy.hdg = hdg;
@@ -257,7 +233,7 @@ bool FGAILocalTraffic::Init(const string& callsign, const string& ICAO, Operatin
 
        //cout << "In Init(), initialState = " << initialState << endl;
        operatingState = initialState;
-       Point3D orthopos;
+       SGVec3d orthopos;
        switch(operatingState) {
        case PARKED:
                tuned_station = ground;
@@ -273,7 +249,7 @@ bool FGAILocalTraffic::Init(const string& callsign, const string& ICAO, Operatin
                vel = 0.0;
                slope = 0.0;
                _pos = ourGate->pos;
-               _pos.setelev(aptElev);
+               _pos.setElevationM(aptElev);
                _hdg = ourGate->heading;
                Transform();
                
@@ -296,10 +272,10 @@ bool FGAILocalTraffic::Init(const string& callsign, const string& ICAO, Operatin
                freeTaxi = true;
                // Set a position and orientation in an approximate place for hold short.
                //cout << "rwy.width = " << rwy.width << '\n';
-               orthopos = Point3D((rwy.width / 2.0 + 10.0) * -1.0, 0.0, 0.0);
+               orthopos = SGVec3d((rwy.width / 2.0 + 10.0) * -1.0, 0.0, 0.0);
                // TODO - set the x pos to be +ve if a RH parallel rwy.
                _pos = ortho.ConvertFromLocal(orthopos);
-               _pos.setelev(aptElev);
+               _pos.setElevationM(aptElev);
                _hdg = rwy.hdg + 90.0;
                // TODO - reset the heading if RH rwy.
                _pitch = 0.0;
@@ -339,8 +315,8 @@ bool FGAILocalTraffic::Init(const string& callsign, const string& ICAO, Operatin
                touchAndGo = false;
 
                if(initialLeg == DOWNWIND) {
-                       _pos = ortho.ConvertFromLocal(Point3D(1000*patternDirection, 800, 0.0));
-                       _pos.setelev(rwy.threshold_pos.elev() + 1000 * SG_FEET_TO_METER);
+                        _pos = ortho.ConvertFromLocal(SGVec3d(1000*patternDirection, 800, 0.0));
+                       _pos.setElevationM(rwy.threshold_pos.getElevationM() + 1000 * SG_FEET_TO_METER);
                        _hdg = rwy.hdg + 180.0;
                        leg = DOWNWIND;
                        elevInitGood = false;
@@ -358,9 +334,7 @@ bool FGAILocalTraffic::Init(const string& callsign, const string& ICAO, Operatin
                        Transform();
                } 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());
+                        _pos = rwy.threshold_pos;
                        _hdg = rwy.hdg;
                        
                        // Now we've set the position we can do the ground elev
@@ -471,7 +445,7 @@ void FGAILocalTraffic::Update(double dt) {
        // 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);
+                if(dclGetHorizontalSeparation(_pos, SGGeod::fromDegM(fgGetDouble("/position/longitude-deg"), fgGetDouble("/position/latitude-deg"), 0.0)) > 8000) _aip.setVisible(false);
                else _aip.setVisible(true);
        } else {
                _aip.setVisible(false);
@@ -572,6 +546,7 @@ void FGAILocalTraffic::Update(double dt) {
                // And to avoid compiler warnings...
                case APPROACH:  break;
                case ATIS:      break;
+    case AWOS:      break;
                case ENROUTE:   break;
                case DEPARTURE: break;
                case INVALID:   break;
@@ -587,8 +562,8 @@ void FGAILocalTraffic::Update(double dt) {
                if(!inAir) {
                        DoGroundElev();
                        if(!elevInitGood) {
-                               if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
-                                       _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
+                               if(_ground_elevation_m > -9990.0) {
+                                       _pos.setElevationM(_ground_elevation_m + wheelOffset);
                                        //cout << "TAKEOFF_ROLL, POS = " << pos.lon() << ", " << pos.lat() << ", " << pos.elev() << '\n';
                                        //Transform();
                                        _aip.setVisible(true);
@@ -605,8 +580,8 @@ void FGAILocalTraffic::Update(double dt) {
                //cout << "*" << flush;
                if(!elevInitGood) {
                        //DoGroundElev();
-                       if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
-                               _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
+                       if(_ground_elevation_m > -9990.0) {
+                               _pos.setElevationM(_ground_elevation_m + wheelOffset);
                                //Transform();
                                _aip.setVisible(true);
                                //Transform();
@@ -632,7 +607,7 @@ void FGAILocalTraffic::Update(double dt) {
                                //cout << "C" << endl;
                                node* np = new node;
                                np->struct_type = NODE;
-                               np->pos = ortho.ConvertFromLocal(Point3D(0.0, 10.0, 0.0));
+                               np->pos = ortho.ConvertFromLocal(SGVec3d(0.0, 10.0, 0.0));
                                path.push_back(np);
                        } else {
                                //cout << "D" << endl;
@@ -665,8 +640,8 @@ void FGAILocalTraffic::Update(double dt) {
                //cout << "In PARKED\n";
                if(!elevInitGood) {
                        DoGroundElev();
-                       if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
-                               _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
+                       if(_ground_elevation_m > -9990.0) {
+                               _pos.setElevationM(_ground_elevation_m + wheelOffset);
                                //Transform();
                                _aip.setVisible(true);
                                //Transform();
@@ -821,8 +796,8 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
        double turn_time = 60.0;        // seconds - TODO - check this guess
        double turn_circumference;
        double turn_radius;
-       Point3D orthopos = ortho.ConvertToLocal(_pos);  // ortho position of the plane
-       //cout << "runway elev = " << rwy.threshold_pos.elev() << ' ' << rwy.threshold_pos.elev() * SG_METER_TO_FEET << '\n';
+       SGVec3d orthopos = ortho.ConvertToLocal(_pos);  // ortho position of the plane
+       //cout << "runway elev = " << rwy.threshold_pos.getElevationM() << ' ' << rwy.threshold_pos.getElevationM() * SG_METER_TO_FEET << '\n';
        //cout << "elev = " << _pos.elev() << ' ' << _pos.elev() * SG_METER_TO_FEET << '\n';
 
        // HACK FOR TESTING - REMOVE
@@ -845,8 +820,8 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                        double dveldt = 5.0;
                        vel += dveldt * dt;
                }
-               if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
-                       _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
+               if(_ground_elevation_m > -9990.0) {
+                       _pos.setElevationM(_ground_elevation_m + wheelOffset);
                }
                IAS = vel + (cos((_hdg - wind_from) * DCL_DEGREES_TO_RADIANS) * wind_speed);
                if(IAS >= 70) {
@@ -864,9 +839,9 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                // (decided in FGTower and accessed through GetCrosswindConstraint(...)).
                // 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) {
+               if((_pos.getElevationM() - rwy.threshold_pos.getElevationM()) * SG_METER_TO_FEET > 700) {
                        double cc = 0.0;
-                       if(tower->GetCrosswindConstraint(cc)) {
+                       if(_controlled && tower->GetCrosswindConstraint(cc)) {
                                if(orthopos.y() > cc) {
                                        //cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n'; 
                                        leg = TURN1;
@@ -879,7 +854,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                }
                // Need to check for levelling off in case we can't turn crosswind as soon
                // as we would like due to other traffic.
-               if((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) {
+               if((_pos.getElevationM() - rwy.threshold_pos.getElevationM()) * SG_METER_TO_FEET > 1000) {
                        slope = 0.0;
                        _pitch = 0.0;
                        IAS = 80.0;             // FIXME - use smooth transistion to new speed and attitude.
@@ -901,7 +876,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                break;
        case CROSSWIND:
                goAround = false;
-               if((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 1000) {
+               if((_pos.getElevationM() - rwy.threshold_pos.getElevationM()) * SG_METER_TO_FEET > 1000) {
                        slope = 0.0;
                        _pitch = 0.0;
                        IAS = 80.0;             // FIXME - use smooth transistion to new speed
@@ -909,7 +884,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                // turn 1000m out for now, taking other traffic into accout
                if(fabs(orthopos.x()) > 900) {
                        double dd = 0.0;
-                       if(tower->GetDownwindConstraint(dd)) {
+                       if(_controlled && tower->GetDownwindConstraint(dd)) {
                                if(fabs(orthopos.x()) > fabs(dd)) {
                                        //cout << "Turning to downwind, distance from centerline = " << fabs(orthopos.x()) << '\n'; 
                                        leg = TURN2;
@@ -923,7 +898,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
        case TURN2:
                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) {
+               if((_pos.getElevationM() - rwy.threshold_pos.getElevationM()) * SG_METER_TO_FEET > 1000) {
                        slope = 0.0;
                        _pitch = 0.0;
                        IAS = 80.0;             // FIXME - use smooth transistion to new speed
@@ -935,12 +910,12 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                break;
        case DOWNWIND:
                // 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)) {
+               if(((_pos.getElevationM() - rwy.threshold_pos.getElevationM()) * SG_METER_TO_FEET > 995) && ((_pos.getElevationM() - rwy.threshold_pos.getElevationM()) * SG_METER_TO_FEET < 1015)) {
                        slope = 0.0;
                        _pitch = 0.0;
                        IAS = 90.0;             // FIXME - use smooth transistion to new speed
                }
-               if((_pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET >= 1015) {
+               if((_pos.getElevationM() - rwy.threshold_pos.getElevationM()) * SG_METER_TO_FEET >= 1015) {
                        slope = -1.0;
                        _pitch = -1.0;
                        IAS = 90.0;             // FIXME - use smooth transistion to new speed
@@ -955,7 +930,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                        // For now we're assuming that we aim to follow the same glidepath regardless of wind.
                        double d1;
                        double d2;
-                       CalculateSoD((tower->GetBaseConstraint(d1) ? d1 : -1000.0), (tower->GetDownwindConstraint(d2) ? d2 : 1000.0 * patternDirection), (patternDirection ? true : false));
+                       CalculateSoD(((_controlled && tower->GetBaseConstraint(d1)) ? d1 : -1000.0), ((_controlled && tower->GetDownwindConstraint(d2)) ? d2 : 1000.0 * patternDirection), (patternDirection ? true : false));
                        if(SoD.leg == DOWNWIND) {
                                descending = (orthopos.y() < SoD.y ? true : false);
                        }
@@ -975,7 +950,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                if(orthopos.y() < -1000.0 + turn_radius) {
                //if(orthopos.y() < -980) {
                        double bb = 0.0;
-                       if(tower->GetBaseConstraint(bb)) {
+                       if(_controlled && tower->GetBaseConstraint(bb)) {
                                if(fabs(orthopos.y()) > fabs(bb)) {
                                        //cout << "Turning to base, distance from threshold = " << fabs(orthopos.y()) << '\n'; 
                                        leg = TURN3;
@@ -1007,7 +982,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                        double d1;
                        // Make downwind leg position artifically large to avoid any chance of SoD being returned as
                        // on downwind when we are already on base.
-                       CalculateSoD((tower->GetBaseConstraint(d1) ? d1 : -1000.0), (10000.0 * patternDirection), (patternDirection ? true : false));
+                       CalculateSoD(((_controlled && tower->GetBaseConstraint(d1)) ? d1 : -1000.0), (10000.0 * patternDirection), (patternDirection ? true : false));
                        if(SoD.leg == BASE) {
                                descending = (fabs(orthopos.y()) < fabs(SoD.y) ? true : false);
                        }
@@ -1065,20 +1040,20 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                if(descending) {
                        if(orthopos.y() < -50.0) {
                                double thesh_offset = 30.0;
-                               slope = atan((_pos.elev() - fgGetAirportElev(airportID)) / (orthopos.y() - thesh_offset)) * DCL_RADIANS_TO_DEGREES;
+                               slope = atan((_pos.getElevationM() - fgGetAirportElev(airportID)) / (orthopos.y() - thesh_offset)) * DCL_RADIANS_TO_DEGREES;
                                //cout << "slope = " << slope << ", elev = " << _pos.elev() << ", apt_elev = " << fgGetAirportElev(airportID) << ", op.y = " << orthopos.y() << '\n';
                                if(slope < -10.0) slope = -10.0;
                                _savedSlope = slope;
                                _pitch = -4.0;
                                IAS = 70.0;
                        } else {
-                               if(_pos.elev() < (rwy.threshold_pos.elev()+10.0+wheelOffset)) {
-                                       if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
-                                               if(_pos.elev() < (_aip.getSGLocation()->get_cur_elev_m() + wheelOffset + 1.0)) {
+                               if(_pos.getElevationM() < (rwy.threshold_pos.getElevationM()+10.0+wheelOffset)) {
+                                       if(_ground_elevation_m > -9990.0) {
+                                               if(_pos.getElevationM() < (_ground_elevation_m + wheelOffset + 1.0)) {
                                                        slope = -2.0;
                                                        _pitch = 1.0;
                                                        IAS = 55.0;
-                                               } else if(_pos.elev() < (_aip.getSGLocation()->get_cur_elev_m() + wheelOffset + 5.0)) {
+                                               } else if(_pos.getElevationM() < (_ground_elevation_m + wheelOffset + 5.0)) {
                                                        slope = -4.0;
                                                        _pitch = -2.0;
                                                        IAS = 60.0;
@@ -1103,15 +1078,15 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                // Try and track the extended centreline
                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)) {
+               if(_pos.getElevationM() < (rwy.threshold_pos.getElevationM()+20.0+wheelOffset)) {
                        DoGroundElev(); // Need to call it here expicitly on final since it's only called
                                        // for us in update(...) when the inAir flag is false.
                }
-               if(_pos.elev() < (rwy.threshold_pos.elev()+10.0+wheelOffset)) {
+               if(_pos.getElevationM() < (rwy.threshold_pos.getElevationM()+10.0+wheelOffset)) {
                        //slope = -1.0;
                        //_pitch = 1.0;
-                       if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
-                               if((_aip.getSGLocation()->get_cur_elev_m() + wheelOffset) > _pos.elev()) {
+                       if(_ground_elevation_m > -9990.0) {
+                               if((_ground_elevation_m + wheelOffset) > _pos.getElevationM()) {
                                        slope = 0.0;
                                        _pitch = 0.0;
                                        leg = LANDING_ROLL;
@@ -1127,8 +1102,8 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
        case LANDING_ROLL:
                //inAir = false;
                descending = false;
-               if(_aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
-                       _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
+               if(_ground_elevation_m > -9990.0) {
+                       _pos.setElevationM(_ground_elevation_m + wheelOffset);
                }
                track = rwy.hdg;
                dveldt = -5.0;
@@ -1152,43 +1127,24 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
     }
 
        if(inAir) {
-               // FIXME - at the moment this is a bit screwy
-               // The velocity correction is applied based on the relative headings.
-               // Then the heading is changed based on the velocity.
-               // Which comes first, the chicken or the egg?
-               // Does it really matter?
-               
-               // Apply wind to ground-relative velocity if in the air
-               vel = IAS - (cos((_hdg - wind_from) * DCL_DEGREES_TO_RADIANS) * wind_speed);
-               //crab = f(track, wind, vel);
-               // The vector we need to fly is our desired vector minus the wind vector
-               // TODO - we probably ought to use plib's built in vector types and operations for this
-               // ie.  There's almost *certainly* a better way to do this!
-               double gxx = vel * sin(track * DCL_DEGREES_TO_RADIANS); // Plane desired velocity x component wrt ground
-               double gyy = vel * cos(track * DCL_DEGREES_TO_RADIANS); // Plane desired velocity y component wrt ground
-               double wxx = wind_speed * sin((wind_from + 180.0) * DCL_DEGREES_TO_RADIANS);    // Wind velocity x component
-               double wyy = wind_speed * cos((wind_from + 180.0) * DCL_DEGREES_TO_RADIANS);    // Wind velocity y component
-               double axx = gxx - wxx; // Plane in-air velocity x component
-               double ayy = gyy - wyy; // Plane in-air velocity y component
-               // Now we want the angle between gxx and axx (which is the crab)
-               double maga = sqrt(axx*axx + ayy*ayy);
-               double magg = sqrt(gxx*gxx + gyy*gyy);
-               crab = acos((axx*gxx + ayy*gyy) / (maga * magg));
-               // At this point this works except we're getting the modulus of the angle
-               //cout << "crab = " << crab << '\n';
-               
-               // Make sure both headings are in the 0->360 circle in order to get sane differences
-               dclBoundHeading(wind_from);
-               dclBoundHeading(track);
-               if(track > wind_from) {
-                       if((track - wind_from) <= 180) {
-                               crab *= -1.0;
-                       }
+               // calculate ground speed and crab from the wind triangle
+               double wind_angle = GetAngleDiff_deg(wind_from + 180, track);
+               double wind_side = (wind_angle < 0) ? -1.0 : 1.0;
+
+               double sine_of_crab = wind_speed / IAS * sin(fabs(wind_angle) * DCL_DEGREES_TO_RADIANS);
+               if (sine_of_crab >= 1.0) {
+                       // The crosswind component is greater than the IAS,
+                       // we can't keep the aircraft on track.
+                       // Assume increased IAS such that it cancels lateral speed.
+                       // This is unrealistic, but not sure how the rest of the sim
+                       // would react to the aircraft going off course.
+                       // Should be a rare case anyway.
+                       crab = wind_side * 90.0;
                } else {
-                       if((wind_from - track) >= 180) {
-                               crab *= -1.0;
-                       }
+                       crab = asin(sine_of_crab) * DCL_RADIANS_TO_DEGREES * wind_side;
                }
+               vel = cos(wind_angle * DCL_DEGREES_TO_RADIANS) * wind_speed
+                       + cos(crab * DCL_DEGREES_TO_RADIANS) * IAS;
        } else {        // on the ground - crab dosen't apply
                crab = 0.0;
        }
@@ -1196,6 +1152,7 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
        //cout << "X " << orthopos.x() << "  Y " << orthopos.y() << "  SLOPE " << slope << "  elev " << _pos.elev() * SG_METER_TO_FEET << '\n';
        
        _hdg = track + crab;
+       dclBoundHeading(_hdg);
        dist = vel * 0.514444 * dt;
        _pos = dclUpdatePosition(_pos, track, slope, dist);
 }
@@ -1232,10 +1189,11 @@ void FGAILocalTraffic::CalculateSoD(double base_leg_pos, double downwind_leg_pos
 
 void FGAILocalTraffic::TransmitPatternPositionReport(void) {
        // airport name + "traffic" + airplane callsign + pattern direction + pattern leg + rwy + ?
-       string trns = "";
+       string trns;
        int code = 0;
-       
-       trns += tower->get_name();
+       const string& apt_name = _controlled ? tower->get_name() : airportID;
+
+       trns += apt_name;
        trns += " Traffic ";
        trns += plane.callsign;
        if(patternDirection == 1) {
@@ -1274,10 +1232,10 @@ void FGAILocalTraffic::TransmitPatternPositionReport(void) {
        }
        trns += ConvertRwyNumToSpokenString(rwy.rwyID);
        
-       trns += " ";
+       trns += ' ';
        
        // And add the airport name again
-       trns += tower->get_name();
+       trns += apt_name;
        
        pending_transmission = trns;
        ConditionalTransmit(60.0, code);                // Assume a report of this leg will be invalid if we can't transmit within a minute.
@@ -1310,7 +1268,7 @@ void FGAILocalTraffic::ProcessCallback(int code) {
        }
 }
 
-void FGAILocalTraffic::ExitRunway(const Point3D& orthopos) {
+void FGAILocalTraffic::ExitRunway(const SGVec3d& orthopos) {
        //cout << "In ExitRunway" << endl;
        //cout << "Runway ID is " << rwy.ID << endl;
        
@@ -1333,7 +1291,7 @@ void FGAILocalTraffic::ExitRunway(const Point3D& orthopos) {
                node* rwyExit = *(exitNodes.begin());
                //int gateID;           //This might want to be more persistant at some point
                while(nItr != exitNodes.end()) {
-                       d = ortho.ConvertToLocal((*nItr)->pos).y() - ortho.ConvertToLocal(_pos).y();    //FIXME - consider making orthopos a class variable
+                        d = ortho.ConvertToLocal((*nItr)->pos).y() - ortho.ConvertToLocal(_pos).y();   //FIXME - consider making orthopos a class variable
                        if(d > 0.0) {
                                if(d < dist) {
                                        dist = d;
@@ -1481,8 +1439,7 @@ void FGAILocalTraffic::Taxi(double dt) {
        // If we have reached turning point then get next point and turn onto that heading
        // Look out for the finish!!
 
-       //Point3D orthopos = ortho.ConvertToLocal(pos); // ortho position of the plane
-       desiredTaxiHeading = GetHeadingFromTo(_pos, nextTaxiNode->pos);
+        desiredTaxiHeading = GetHeadingFromTo(_pos, nextTaxiNode->pos);
        
        bool lastNode = (taxiPathPos == path.size() ? true : false);
        if(lastNode) {
@@ -1514,8 +1471,8 @@ void FGAILocalTraffic::Taxi(double dt) {
                double slope = 0.0;
                _pos = dclUpdatePosition(_pos, track, slope, dist);
                //cout << "Updated position...\n";
-               if(_aip.getSGLocation()->get_cur_elev_m() > -9990) {
-                       _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
+               if(_ground_elevation_m > -9990) {
+                       _pos.setElevationM(_ground_elevation_m + wheelOffset);
                } // else don't change the elev until we get a valid ground elev again!
        } else if(lastNode) {
                if(taxiState == TD_LINING_UP) {
@@ -1533,8 +1490,8 @@ void FGAILocalTraffic::Taxi(double dt) {
                                double slope = 0.0;
                                _pos = dclUpdatePosition(_pos, track, slope, dist);
                                //cout << "Updated position...\n";
-                               if(_aip.getSGLocation()->get_cur_elev_m() > -9990) {
-                                       _pos.setelev(_aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
+                               if(_ground_elevation_m > -9990) {
+                                       _pos.setElevationM(_ground_elevation_m + wheelOffset);
                                } // else don't change the elev until we get a valid ground elev again!
                                if(fabs(_hdg - rwy.hdg) <= 1.0) {
                                        operatingState = IN_PATTERN;
@@ -1563,32 +1520,24 @@ 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");
        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);
+       if(dclGetHorizontalSeparation(_pos, SGGeod::fromGeodM(vw->getPosition(), 0.0)) > visibility_meters) {
+               _ground_elevation_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 range = 500.0;
+  if (!globals->get_tile_mgr()->scenery_available(_aip.getPosition(), range)) {
+    // Try to shedule tiles for that position.
+    globals->get_tile_mgr()->update( _aip.getPosition(), 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, 0))
-          _aip.getSGLocation()->set_cur_elev_m(alt);
+  // FIXME: make shure the pos.lat/pos.lon values are in degrees ...
+  double alt;
+  if (globals->get_scenery()->get_elevation_m(SGGeod::fromGeodM(_aip.getPosition(), 20000), alt, 0, _aip.getSceneGraph()))
+    _ground_elevation_m = alt;
 }