]> 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 f4dc8dbeae79d01d0be2073b1bf5b7683f1dc8bf..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
 
+#include <simgear/scene/model/location.hxx>
+
 #include <Airports/runways.hxx>
 #include <Main/globals.hxx>
-#include <Main/location.hxx>
 #include <Scenery/scenery.hxx>
 #include <Scenery/tilemgr.hxx>
 #include <simgear/math/point3d.hxx>
@@ -41,6 +53,12 @@ SG_USING_STD(string);
 #include "ATCutils.hxx"
 
 FGAILocalTraffic::FGAILocalTraffic() {
+       ATC = globals->get_ATC_mgr();
+       
+       // TODO - unhardwire this - possibly let the AI manager set the callsign
+       plane.callsign = "Trainer-two-five-charlie";
+       plane.type = GA_SINGLE;
+       
        roll = 0.0;
        pitch = 0.0;
        hdg = 270.0;
@@ -58,15 +76,28 @@ FGAILocalTraffic::FGAILocalTraffic() {
        nominal_final_speed = 65.0;
        //nominal_approach_speed;
        //stall_speed_landing_config;
-       nominalTaxiSpeed = 8.0;
+       nominalTaxiSpeed = 7.5;
        taxiTurnRadius = 8.0;
        wheelOffset = 1.45;     // Warning - hardwired to the C172 - we need to read this in from file.
        elevInitGood = false;
        // Init the property nodes
        wind_from_hdg = fgGetNode("/environment/wind-from-heading-deg", true);
-       wind_speed_knots = fgGetNode("/environment/wind-speed-kts", true);
+       wind_speed_knots = fgGetNode("/environment/wind-speed-kt", true);
        circuitsToFly = 0;
        liningUp = false;
+       taxiRequestPending = false;
+       taxiRequestCleared = false;
+       holdingShort = false;
+       clearedToLineUp = false;
+       clearedToTakeOff = false;
+       reportReadyForDeparture = false;
+       contactTower = false;
+       contactGround = false;
+       
+       descending = false;
+       targetDescentRate = 0.0;
+       goAround = false;
+       goAroundCalled = false;
 }
 
 FGAILocalTraffic::~FGAILocalTraffic() {
@@ -74,28 +105,20 @@ FGAILocalTraffic::~FGAILocalTraffic() {
 
 
 // Get details of the active runway
-// This is a private internal function and it is assumed that by the 
-// time it is called the tower control and airport code will have been set up.
+// It is assumed that by the time this is called the tower control and airport code will have been set up.
 void FGAILocalTraffic::GetRwyDetails() {
        //cout << "GetRwyDetails called" << endl;
        
-       // Based on the airport-id and wind get the active runway
-       SGPath path( globals->get_fg_root() );
-       path.append( "Airports" );
-       path.append( "runways.mk4" );
-       FGRunways runways( path.c_str() );
+       rwy.rwyID = tower->GetActiveRunway();
        
-       //wind
-       double hdg = wind_from_hdg->getDoubleValue();
-       double speed = wind_speed_knots->getDoubleValue();
-       hdg = (speed == 0.0 ? 270.0 : hdg);
-       //cout << "Heading = " << hdg << '\n';
+       // Now we need to get the threshold position and rwy heading
        
        FGRunway runway;
-       bool rwyGood = runways.search(airportID, int(hdg), &runway);
+       bool rwyGood = globals->get_runways()->search(airportID, rwy.rwyID,
+                                                      &runway);
        if(rwyGood) {
                // Get the threshold position
-       hdg = runway.heading;
+       hdg = runway.heading;   // TODO - check - is this our heading we are setting here, and if so should we be?
                //cout << "hdg reset to " << hdg << '\n';
                double other_way = hdg - 180.0;
                while(other_way <= 0.0) {
@@ -120,21 +143,16 @@ void FGAILocalTraffic::GetRwyDetails() {
                //cout << "Threshold position = " << tshlon << ", " << tshlat << ", " << aptElev << '\n';
                //cout << "Takeoff position = " << tolon << ", " << tolat << ", " << aptElev << '\n';
                rwy.hdg = hdg;
-               rwy.rwyID = runway.rwy_no;
                // Set the projection for the local area
                ortho.Init(rwy.threshold_pos, rwy.hdg); 
                rwy.end1ortho = ortho.ConvertToLocal(rwy.threshold_pos);        // should come out as zero
                rwy.end2ortho = ortho.ConvertToLocal(takeoff_end);
-               rwy.mag_var = 14.0;             // TODO - remove this last hardwired bit!! 
-               //(Although I don't think we even use the magvar any more?)
-               rwy.mag_hdg = rwy.hdg - rwy.mag_var;
-               rwy.ID = (int)rwy.mag_hdg / 10;         
-               //cout << "rwy.ID = " << rwy.ID << '\n';
        } else {
-               SG_LOG(SG_GENERAL, SG_ALERT, "Help  - can't get good runway in FGAILocalTraffic!!\n");
+               SG_LOG(SG_ATC, SG_ALERT, "Help  - can't get good runway in FGAILocalTraffic!!\n");
        }
 }
 
+
 /* 
 There are two possible scenarios during initialisation:
 The first is that the user is flying towards the airport, and hence the traffic
@@ -144,54 +162,66 @@ 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;
        // Hack alert - Hardwired path!!
        string planepath = "Aircraft/c172/Models/c172-dpm.ac";
-       SGPath path = globals->get_fg_root();
-       path.append(planepath);
-       aip.init(planepath.c_str());
+       ssgBranch *model = sgLoad3DModel( globals->get_fg_root(),
+                                         planepath.c_str(),
+                                         globals->get_props(),
+                                         globals->get_sim_time_sec() );
+       aip.init( model );
        aip.setVisible(false);          // This will be set to true once a valid ground elevation has been determined
        globals->get_scenery()->get_scene_graph()->addKid(aip.getSceneGraph());
        
        // Find the tower frequency - this is dependent on the ATC system being initialised before the AI system
        airportID = ICAO;
        AirportATC a;
-       if(globals->get_ATC_mgr()->GetAirportATCDetails(airportID, &a)) {
+       if(ATC->GetAirportATCDetails(airportID, &a)) {
                if(a.tower_freq) {      // Has a tower
-                       tower = (FGTower*)globals->get_ATC_mgr()->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);
+                       }
                        freq = (double)tower->get_freq() / 100.0;
-                       //cout << "***********************************AILocalTraffic freq = " << freq << '\n';
+                       ground = tower->GetGroundPtr();
+                       if(ground == NULL) {
+                               // Something has gone wrong :-(
+                               SG_LOG(SG_ATC, SG_ALERT, "ERROR - can't get a ground pointer from tower control in FGAILocalTraffic::Init() :-(");
+                               return(false);
+                       } else if((initialState == PARKED) || (initialState == TAXIING)) {
+                               freq = (double)ground->get_freq() / 100.0;
+                       }
+                       //cout << "AILocalTraffic freq is " << freq << '\n';
                } else {
-                       // Check CTAF, unicom etc
+                       // TODO - Check CTAF, unicom etc
                }
        } else {
                //cout << "Unable to find airport details in FGAILocalTraffic::Init()\n";
        }
-
-       // Initialise the relevant FGGround
-       // This needs a complete overhaul soon - what happens if we have 2 AI planes at same airport - they don't both need a structure
-       // This needs to be handled by the ATC manager or similar so only one set of physical data per airport is instantiated
-       // ie. TODO TODO FIXME FIXME
-       airport.Init();
        
+               // Get the active runway details (and copy them into rwy)
+               GetRwyDetails();
+
        // Get the airport elevation
        aptElev = dclGetAirportElev(airportID.c_str()) * SG_FEET_TO_METER;
        //cout << "Airport elev in AILocalTraffic = " << aptElev << '\n';
        // WARNING - we use this elev for the whole airport - some assumptions in the code 
        // might fall down with very slopey airports.
 
-       //cout << "In Init(), initialState = " << initialState << '\n';
+       //cout << "In Init(), initialState = " << initialState << endl;
        operatingState = initialState;
        switch(operatingState) {
        case PARKED:
-               ourGate = airport.GetGateNode();
+               tuned_station = ground;
+               ourGate = ground->GetGateNode();
                if(ourGate == NULL) {
                        // Implies no available gates - what shall we do?
                        // For now just vanish the plane - possibly we can make this more elegant in the future
-                       SG_LOG(SG_GENERAL, SG_ALERT, "No gate found by FGAILocalTraffic whilst attempting Init at " << airportID << '\n');
+                       SG_LOG(SG_ATC, SG_ALERT, "No gate found by FGAILocalTraffic whilst attempting Init at " << airportID << '\n');
                        return(false);
                }
                pitch = 0.0;
@@ -210,6 +240,7 @@ bool FGAILocalTraffic::Init(string ICAO, OperatingState initialState, PatternLeg
                Transform();
                break;
        case TAXIING:
+               tuned_station = ground;
                // FIXME - implement this case properly
                return(false);  // remove this line when fixed!
                break;
@@ -218,26 +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.
                
-               // 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;
@@ -247,13 +263,48 @@ 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();
                break;
        default:
-               SG_LOG(SG_GENERAL, SG_ALERT, "Attempt to set unknown operating state in FGAILocalTraffic.Init(...)\n");
+               SG_LOG(SG_ATC, SG_ALERT, "Attempt to set unknown operating state in FGAILocalTraffic.Init(...)\n");
                return(false);
        }
        
@@ -261,6 +312,18 @@ bool FGAILocalTraffic::Init(string ICAO, OperatingState initialState, PatternLeg
        return(true);
 }
 
+
+// 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 {
+               return(FULL_STOP);
+       }
+}
+       
+
 // Commands to do something from higher level logic
 void FGAILocalTraffic::FlyCircuits(int numCircuits, bool tag) {
        //cout << "FlyCircuits called" << endl;
@@ -271,83 +334,87 @@ void FGAILocalTraffic::FlyCircuits(int numCircuits, bool tag) {
                return;
                break;
        case TAXIING:
-               // For now we'll punt this and do nothing
+               // TODO - For now we'll punt this and do nothing
                break;
        case PARKED:
-               circuitsToFly = numCircuits - 1;        // Hack (-1) because we only test and decrement circuitsToFly after landing
+               circuitsToFly = numCircuits;    // Note that one too many circuits gets flown because we only test and decrement circuitsToFly after landing
                                                                                // thus flying one too many circuits.  TODO - Need to sort this out better!
                touchAndGo = tag;
-       
-               // Get the active runway details (and copy them into rwy)
-               GetRwyDetails();
-               
-               // Get the takeoff node for the active runway, get a path to it and start taxiing
-               path = airport.GetPath(ourGate, rwy.rwyID);
-               if(path.size() < 2) {
-                       // something has gone wrong
-                       SG_LOG(SG_GENERAL, SG_ALERT, "Invalid path from gate to theshold in FGAILocalTraffic::FlyCircuits\n");
-                       return;
-               }
-               /*
-               cout << "path returned was:" << endl;
-               for(unsigned int i=0; i<path.size(); ++i) {
-                       switch(path[i]->struct_type) {
-                       case NODE:
-                               cout << "NODE " << ((node*)(path[i]))->nodeID << endl;
-                               break;
-                       case ARC:
-                               cout << "ARC\n";
-                               break;
-                       }
-               }
-               */
-               // pop the gate - we're here already!
-               path.erase(path.begin());
-               //path.erase(path.begin());
-               /*
-               cout << "path after popping front is:" << endl;
-               for(unsigned int i=0; i<path.size(); ++i) {
-                       switch(path[i]->struct_type) {
-                       case NODE:
-                               cout << "NODE " << ((node*)(path[i]))->nodeID << endl;
-                               break;
-                       case ARC:
-                               cout << "ARC\n";
-                               break;
-                       }
-               }
-               */
-               
-               taxiState = TD_OUTBOUND;
-               StartTaxi();
-               
-               // Maybe the below should be set when we get to the threshold and prepare for TO?
-               // FIXME TODO - pattern direction is still hardwired
-               patternDirection = -1;          // Left
-               // At the bare minimum we ought to make sure it goes the right way at dual parallel rwy airports!
-               if(rwy.rwyID.size() == 3) {
-                       patternDirection = (rwy.rwyID.substr(2,1) == "R" ? 1 : -1);
-               }
-
-               Transform();
                break;
        }
 }   
 
 // Run the internal calculations
 void FGAILocalTraffic::Update(double dt) {
+       //cout << "A" << flush;
+       //double responseTime = 10.0;           // seconds - this should get more sophisticated at some point
+       responseCounter += dt;
+       if((contactTower) && (responseCounter >= 8.0)) {
+               // Acknowledge request before changing frequency so it gets rendered if the user is on the same freq
+               string trns = "Tower ";
+               double f = globals->get_ATC_mgr()->GetFrequency(airportID, TOWER) / 100.0;      
+               char buf[10];
+               sprintf(buf, "%.2f", f);
+               trns += buf;
+               trns += " ";
+               trns += plane.callsign;
+               pending_transmission = trns;
+               ConditionalTransmit(30.0);
+               responseCounter = 0.0;
+               contactTower = false;
+               changeFreq = true;
+               changeFreqType = TOWER;
+       }
+       
+       if((changeFreq) && (responseCounter > 8.0)) {
+               switch(changeFreqType) {
+               case TOWER:
+                       tuned_station = tower;
+                       freq = (double)tower->get_freq() / 100.0;
+                       //Transmit("DING!");
+                       // Contact the tower, even if only virtually
+                       changeFreq = false;
+                       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;
+                       freq = (double)ground->get_freq() / 100.0;
+                       break;
+               // And to avoid compiler warnings...
+               case APPROACH:  break;
+               case ATIS:      break;
+               case ENROUTE:   break;
+               case DEPARTURE: break;
+               case INVALID:   break;
+               }
+       }
+       
+       //cout << "." << flush;
+               
        switch(operatingState) {
        case IN_PATTERN:
                //cout << "In IN_PATTERN\n";
-               if(!inAir) DoGroundElev();
-               if(!elevInitGood) {
-                       if(aip.getFGLocation()->get_cur_elev_m() > -9990.0) {
-                               pos.setelev(aip.getFGLocation()->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);
@@ -355,10 +422,11 @@ void FGAILocalTraffic::Update(double dt) {
                break;
        case TAXIING:
                //cout << "In TAXIING\n";
+               //cout << "*" << flush;
                if(!elevInitGood) {
                        //DoGroundElev();
-                       if(aip.getFGLocation()->get_cur_elev_m() > -9990.0) {
-                               pos.setelev(aip.getFGLocation()->get_cur_elev_m() + wheelOffset);
+                       if(aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
+                               pos.setelev(aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
                                //Transform();
                                aip.setVisible(true);
                                //Transform();
@@ -367,15 +435,47 @@ void FGAILocalTraffic::Update(double dt) {
                        }
                }
                DoGroundElev();
-               Taxi(dt);
+               //cout << "," << flush;
+               if(!((holdingShort) && (!clearedToLineUp))) {
+                       //cout << "|" << flush;
+                       Taxi(dt);
+               }
+               //cout << ";" << flush;
+               if((clearedToTakeOff) && (responseCounter >= 8.0)) {
+                       // possible assumption that we're at the hold short here - may not always hold
+                       // TODO - sort out the case where we're cleared to line-up first and then cleared to take-off on the rwy.
+                       taxiState = TD_LINING_UP;
+                       path = ground->GetPath(holdShortNode, rwy.rwyID);
+                       /*
+                       cout << "path returned was:" << endl;
+                       for(unsigned int i=0; i<path.size(); ++i) {
+                               switch(path[i]->struct_type) {
+                                       case NODE:
+                                       cout << "NODE " << ((node*)(path[i]))->nodeID << endl;
+                                       break;
+                                       case ARC:
+                                       cout << "ARC\n";
+                                       break;
+                               }
+                       }
+                       */
+                       clearedToTakeOff = false;       // We *are* still cleared - this simply stops the response recurring!!
+                       holdingShort = false;
+                       string trns = "Cleared for take-off ";
+                       trns += plane.callsign;
+                       pending_transmission = trns;
+                       Transmit();
+                       StartTaxi();
+               }
+               //cout << "^" << flush;
                Transform();
                break;
        case PARKED:
                //cout << "In PARKED\n";
                if(!elevInitGood) {
                        DoGroundElev();
-                       if(aip.getFGLocation()->get_cur_elev_m() > -9990.0) {
-                               pos.setelev(aip.getFGLocation()->get_cur_elev_m() + wheelOffset);
+                       if(aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
+                               pos.setelev(aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
                                //Transform();
                                aip.setVisible(true);
                                //Transform();
@@ -383,8 +483,107 @@ void FGAILocalTraffic::Update(double dt) {
                                elevInitGood = true;
                        }
                }
+               
+               if(circuitsToFly) {
+                       if((taxiRequestPending) && (taxiRequestCleared)) {
+                               //cout << "&" << flush;
+                               // 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
+                               path = ground->GetPathToHoldShort(ourGate, rwy.rwyID);
+                               if(path.size() < 2) {
+                                       // something has gone wrong
+                                       SG_LOG(SG_ATC, SG_ALERT, "Invalid path from gate to theshold in FGAILocalTraffic::FlyCircuits\n");
+                                       return;
+                               }
+                               /*
+                               cout << "path returned was:\n";
+                               for(unsigned int i=0; i<path.size(); ++i) {
+                                       switch(path[i]->struct_type) {
+                                               case NODE:
+                                               cout << "NODE " << ((node*)(path[i]))->nodeID << endl;
+                                               break;
+                                               case ARC:
+                                               cout << "ARC\n";
+                                               break;
+                                       }
+                               }
+                               */
+                               path.erase(path.begin());       // pop the gate - we're here already!
+                               taxiState = TD_OUTBOUND;
+                               taxiRequestPending = false;
+                               holdShortNode = (node*)(*(path.begin() + path.size()));
+                               StartTaxi();
+                       } else if(!taxiRequestPending) {
+                               //cout << "(" << flush;
+                               // Do some communication
+                               // airport name + tower + airplane callsign + location + request taxi for + operation type + ?
+                               string trns = "";
+                               trns += tower->get_name();
+                               trns += " tower ";
+                               trns += plane.callsign;
+                               trns += " on apron parking request taxi for traffic pattern";
+                               //cout << "trns = " << trns << endl;
+                               pending_transmission = trns;
+                               Transmit(1);
+                               taxiRequestCleared = false;
+                               taxiRequestPending = true;
+                       }
+               }
+               
+               //cout << "!" << flush;
+                               
+               // Maybe the below should be set when we get to the threshold and prepare for TO?
+               // FIXME TODO - pattern direction is still hardwired
+               patternDirection = -1;          // Left
+               // At the bare minimum we ought to make sure it goes the right way at dual parallel rwy airports!
+               if(rwy.rwyID.size() == 3) {
+                       patternDirection = (rwy.rwyID.substr(2,1) == "R" ? 1 : -1);
+               }               
                // Do nothing
                Transform();
+               //cout << ")" << flush;
+               break;
+       default:
+               break;
+       }
+       //cout << "I " << flush;
+       
+       // Convienience output for AI debugging user the property logger
+       fgSetDouble("/AI/Local1/ortho-x", (ortho.ConvertToLocal(pos)).x());
+       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
+       FGAIPlane::Update(dt);
+}
+
+void FGAILocalTraffic::RegisterTransmission(int code) {
+       switch(code) {
+       case 1: // taxi request cleared
+               taxiRequestCleared = true;
+               SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " cleared to taxi...");
+               break;
+       case 2: // contact tower
+               responseCounter = 0;
+               contactTower = true;
+               SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " told to contact tower...");
+               break;
+       case 3: // Cleared to line up
+               responseCounter = 0;
+               clearedToLineUp = true;
+               SG_LOG(SG_ATC, SG_INFO, "AI local traffic " << plane.callsign << " cleared to line-up...");
+               break;
+       case 4: // cleared to take-off
+               responseCounter = 0;
+               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;
@@ -424,6 +623,8 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
        double wind_from = wind_from_hdg->getDoubleValue();
        double wind_speed = wind_speed_knots->getDoubleValue();
 
+       double dveldt;
+       
        switch(leg) {
        case TAKEOFF_ROLL:
                //inAir = false;
@@ -432,23 +633,53 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                        double dveldt = 5.0;
                        vel += dveldt * dt;
                }
-               if(aip.getFGLocation()->get_cur_elev_m() > -9990.0) {
-                       pos.setelev(aip.getFGLocation()->get_cur_elev_m() + wheelOffset);
+               if(aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
+                       pos.setelev(aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
                }
                IAS = vel + (cos((hdg - wind_from) * DCL_DEGREES_TO_RADIANS) * wind_speed);
                if(IAS >= 70) {
                        leg = CLIMBOUT;
                        pitch = 10.0;
                        IAS = best_rate_of_climb_speed;
-                       slope = 7.0;
+                       //slope = 7.0;  
+                       slope = 6.0;    // Reduced it slightly since it's climbing a lot steeper than I can in the JSBSim C172.
                        inAir = true;
                }
                break;
        case CLIMBOUT:
                track = rwy.hdg;
-               if((pos.elev() - rwy.threshold_pos.elev()) * SG_METER_TO_FEET > 600) {
-                       leg = TURN1;
+               // 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.
+               // 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 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;
+                       }
+               }
+               // 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) {
+                       slope = 0.0;
+                       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;
@@ -458,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) {
@@ -465,9 +697,18 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                        pitch = 0.0;
                        IAS = 80.0;             // FIXME - use smooth transistion to new speed
                }
-               // turn 1000m out for now
+               // turn 1000m out for now, taking other traffic into accout
                if(fabs(orthopos.x()) > 980) {
-                       leg = TURN2;
+                       double dd = 0.0;
+                       if(tower->GetDownwindConstraint(dd)) {
+                               if(fabs(orthopos.x()) > fabs(dd)) {
+                                       cout << "Turning to downwind, distance from centerline = " << fabs(orthopos.x()) << '\n'; 
+                                       leg = TURN2;
+                               }
+                       } else {
+                               cout << "Turning to downwind, distance from centerline = " << fabs(orthopos.x()) << '\n'; 
+                               leg = TURN2;
+                       }
                }
                break;
        case TURN2:
@@ -498,16 +739,44 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                        TransmitPatternPositionReport();
                        transmitted = true;
                }
-               if(orthopos.y() < -480) {
-                       slope = -4.0;   // FIXME - calculate to descent at 500fpm and hit the threshold (taking wind into account as well!!)
+               if((orthopos.y() < -100) && (!descending)) {
+                       // Maybe we should think about when to start descending.
+                       // 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));
+                       if(SoD.leg == DOWNWIND) {
+                               descending = (orthopos.y() < SoD.y ? true : false);
+                       }
+
+               }
+               if(descending) {
+                       slope = -5.5;   // FIXME - calculate to descent at 500fpm and hit the desired point on the runway (taking wind into account as well!!)
                        pitch = -3.0;
                        IAS = 85.0;
                }
-               if(orthopos.y() < -980) {
-                       //roll = -20;
-                       leg = TURN3;
-                       transmitted = false;
-                       IAS = 80.0;
+               
+               // Try and arrange to turn nicely onto base
+               turn_circumference = IAS * 0.514444 * turn_time;        
+               //Hmmm - this is an interesting one - ground vs airspeed in relation to turn radius
+               //We'll leave it as a hack with IAS for now but it needs revisiting.            
+               turn_radius = turn_circumference / (2.0 * DCL_PI);
+               if(orthopos.y() < -1000.0 + turn_radius) {
+               //if(orthopos.y() < -980) {
+                       double bb = 0.0;
+                       if(tower->GetBaseConstraint(bb)) {
+                               if(fabs(orthopos.y()) > fabs(bb)) {
+                                       cout << "Turning to base, distance from threshold = " << fabs(orthopos.y()) << '\n'; 
+                                       leg = TURN3;
+                                       transmitted = false;
+                                       IAS = 80.0;
+                               }
+                       } else {
+                               cout << "Turning to base, distance from threshold = " << fabs(orthopos.y()) << '\n'; 
+                               leg = TURN3;
+                               transmitted = false;
+                               IAS = 80.0;
+                       }
                }
                break;
        case TURN3:
@@ -523,15 +792,29 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                        TransmitPatternPositionReport();
                        transmitted = true;
                }
+               
+               if(!descending) {
+                       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));
+                       if(SoD.leg == BASE) {
+                               descending = (fabs(orthopos.y()) < fabs(SoD.y) ? true : false);
+                       }
+
+               }
+               if(descending) {
+                       slope = -5.5;   // FIXME - calculate to descent at 500fpm and hit the threshold (taking wind into account as well!!)
+                       pitch = -4.0;
+                       IAS = 70.0;
+               }
+               
                track = rwy.hdg - (90 * patternDirection);
-               slope = -6.0;   // FIXME - calculate to descent at 500fpm and hit the threshold
-               pitch = -4.0;
-               IAS = 70.0;     // FIXME - slowdown gradually
-               // Try and arrange to turn nicely onto base
+
+               // 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
-               //We'll leave it as a hack with IAS for now but it needs revisiting.
-               
+               //We'll leave it as a hack with IAS for now but it needs revisiting.            
                turn_radius = turn_circumference / (2.0 * DCL_PI);
                if(fabs(orthopos.x()) < (turn_radius + 50)) {
                        leg = TURN4;
@@ -548,11 +831,34 @@ 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();
                        transmitted = true;
                }
+               if(!descending) {
+                       // Make base leg position artifically large to avoid any chance of SoD being returned as
+                       // on base or downwind when we are already on final.
+                       CalculateSoD(-10000.0, (1000.0 * patternDirection), (patternDirection ? true : false));
+                       if(SoD.leg == FINAL) {
+                               descending = (fabs(orthopos.y()) < fabs(SoD.y) ? true : false);
+                       }
+
+               }
+               if(descending) {
+                       slope = -5.5;   // FIXME - calculate to descent at 500fpm and hit the threshold (taking wind into account as well!!)
+                       pitch = -4.0;
+                       IAS = 70.0;
+               }
                // Try and track the extended centreline
                track = rwy.hdg - (0.2 * orthopos.x());
                //cout << "orthopos.x() = " << orthopos.x() << " hdg = " << hdg << '\n';
@@ -561,8 +867,8 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                                        // for us in update(...) when the inAir flag is false.
                }
                if(pos.elev() < (rwy.threshold_pos.elev()+10.0+wheelOffset)) {
-                       if(aip.getFGLocation()->get_cur_elev_m() > -9990.0) {
-                               if((aip.getFGLocation()->get_cur_elev_m() + wheelOffset) > pos.elev()) {
+                       if(aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
+                               if((aip.getSGLocation()->get_cur_elev_m() + wheelOffset) > pos.elev()) {
                                        slope = 0.0;
                                        pitch = 0.0;
                                        leg = LANDING_ROLL;
@@ -573,11 +879,12 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                break;
        case LANDING_ROLL:
                //inAir = false;
-               if(aip.getFGLocation()->get_cur_elev_m() > -9990.0) {
-                       pos.setelev(aip.getFGLocation()->get_cur_elev_m() + wheelOffset);
+               descending = false;
+               if(aip.getSGLocation()->get_cur_elev_m() > -9990.0) {
+                       pos.setelev(aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
                }
                track = rwy.hdg;
-               double dveldt = -5.0;
+               dveldt = -5.0;
                vel += dveldt * dt;
                // FIXME - differentiate between touch and go and full stops
                if(vel <= 15.0) {
@@ -593,6 +900,8 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
                        }
                }
                break;
+       case LEG_UNKNOWN:
+               break;
     }
 
        if(inAir) {
@@ -642,18 +951,48 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
        pos = dclUpdatePosition(pos, track, slope, dist);
 }
 
+// Pattern direction is true for right, false for left
+void FGAILocalTraffic::CalculateSoD(double base_leg_pos, double downwind_leg_pos, bool pattern_direction) {
+       // For now we'll ignore wind and hardwire the glide angle.
+       double ga = 5.5;        //degrees
+       double pa = 1000.0 * SG_FEET_TO_METER;  // pattern altitude in meters
+       // FIXME - get glideslope angle and pattern altitude agl from airport details if available
+       
+       // For convienience, we'll have +ve versions of the input distances
+       double blp = fabs(base_leg_pos);
+       double dlp = fabs(downwind_leg_pos);
+       
+       //double turn_allowance = 150.0;        // Approximate distance in meters that a 90deg corner is shortened by turned in a light plane.
+       
+       double stod = pa / tan(ga * DCL_DEGREES_TO_RADIANS);    // distance in meters from touchdown point to start descent
+       //cout << "Descent to start = " << stod << " meters out\n";
+       if(stod < blp) {        // Start descending on final
+               SoD.leg = FINAL;
+               SoD.y = stod * -1.0;
+               SoD.x = 0.0;
+       } else if(stod < (blp + dlp)) { // Start descending on base leg
+               SoD.leg = BASE;
+               SoD.y = blp * -1.0;
+               SoD.x = (pattern_direction ? (stod - dlp) : (stod - dlp) * -1.0);
+       } else {        // Start descending on downwind leg
+               SoD.leg = DOWNWIND;
+               SoD.x = (pattern_direction ? dlp : dlp * -1.0);
+               SoD.y = (blp - (stod - (blp + dlp))) * -1.0;
+       }
+}
+
 void FGAILocalTraffic::TransmitPatternPositionReport(void) {
        // airport name + "traffic" + airplane callsign + pattern direction + pattern leg + rwy + ?
        string trns = "";
+       int code = 0;
        
        trns += tower->get_name();
        trns += " Traffic ";
-       // FIXME - add the callsign to the class variables
-       trns += "Trainer-two-five-charlie ";
+       trns += plane.callsign;
        if(patternDirection == 1) {
-               trns += "right ";
+               trns += " right ";
        } else {
-               trns += "left ";
+               trns += " left ";
        }
        
        // We could probably get rid of this whole switch statement and just pass a string containing the leg from the FlyPattern function.
@@ -667,6 +1006,7 @@ void FGAILocalTraffic::TransmitPatternPositionReport(void) {
                // Fall through to DOWNWIND
        case DOWNWIND:
                trns += "downwind ";
+               code = 11;
                break;
        case TURN3:
                // Fall through to BASE
@@ -677,24 +1017,45 @@ 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();
        
-       Transmit(trns);
+       pending_transmission = trns;    // FIXME - make up pending_transmission natively        
+       ConditionalTransmit(90.0, code);                // Assume a report of this leg will be invalid if we can't transmit within a minute and a half.
+}
+
+// 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);
+       }
 }
 
 void FGAILocalTraffic::ExitRunway(Point3D orthopos) {
        //cout << "In ExitRunway" << endl;
        //cout << "Runway ID is " << rwy.ID << endl;
-       node_array_type exitNodes = airport.GetExits(rwy.ID);   //I suppose we ought to have some fallback for rwy with no defined exits?
+       node_array_type exitNodes = ground->GetExits(rwy.rwyID);        //I suppose we ought to have some fallback for rwy with no defined exits?
        /*
        cout << "Node ID's of exits are ";
        for(unsigned int i=0; i<exitNodes.size(); ++i) {
@@ -725,16 +1086,16 @@ void FGAILocalTraffic::ExitRunway(Point3D orthopos) {
                        }
                        ++nItr;
                }
-               ourGate = airport.GetGateNode();
+               ourGate = ground->GetGateNode();
                if(ourGate == NULL) {
                        // Implies no available gates - what shall we do?
                        // For now just vanish the plane - possibly we can make this more elegant in the future
-                       SG_LOG(SG_GENERAL, SG_ALERT, "No gate found by FGAILocalTraffic whilst landing at " << airportID << '\n');
+                       SG_LOG(SG_ATC, SG_ALERT, "No gate found by FGAILocalTraffic whilst landing at " << airportID << '\n');
                        aip.setVisible(false);
                        operatingState = PARKED;
                        return;
                }
-               path = airport.GetPath(rwyExit, ourGate);
+               path = ground->GetPath(rwyExit, ourGate);
                /*
                cout << "path returned was:" << endl;
                for(unsigned int i=0; i<path.size(); ++i) {
@@ -752,7 +1113,7 @@ void FGAILocalTraffic::ExitRunway(Point3D orthopos) {
                StartTaxi();
        } 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_GENERAL, SG_ALERT, "No exits found by FGAILocalTraffic from runway " << rwy.ID << " at " << airportID << '\n');
+               SG_LOG(SG_ATC, SG_ALERT, "No exits found by FGAILocalTraffic from runway " << rwy.rwyID << " at " << airportID << '\n');
                // What shall we do - just remove the plane from sight?
                aip.setVisible(false);
                operatingState = PARKED;
@@ -767,7 +1128,7 @@ void FGAILocalTraffic::GetNextTaxiNode() {
        //cout << "taxiPathPos = " << taxiPathPos << endl;
        ground_network_path_iterator pathItr = path.begin() + taxiPathPos;
        if(pathItr == path.end()) {
-               SG_LOG(SG_GENERAL, SG_ALERT, "ERROR IN AILocalTraffic::GetNextTaxiNode - no more nodes in path\n");
+               SG_LOG(SG_ATC, SG_ALERT, "ERROR IN AILocalTraffic::GetNextTaxiNode - no more nodes in path\n");
        } else {
                if((*pathItr)->struct_type == NODE) {
                        //cout << "ITS A NODE" << endl;
@@ -782,13 +1143,13 @@ void FGAILocalTraffic::GetNextTaxiNode() {
                        pathItr++;
                        taxiPathPos++;
                        if(pathItr == path.end()) {
-                               SG_LOG(SG_GENERAL, SG_ALERT, "ERROR IN AILocalTraffic::GetNextTaxiNode - path ended with an arc\n");
+                               SG_LOG(SG_ATC, SG_ALERT, "ERROR IN AILocalTraffic::GetNextTaxiNode - path ended with an arc\n");
                        } else if((*pathItr)->struct_type == NODE) {
                                nextTaxiNode = (node*)*pathItr;
                                ++taxiPathPos;
                        } else {
                                //OOPS - two non-nodes in a row - that shouldn't happen ATM
-                               SG_LOG(SG_GENERAL, SG_ALERT, "ERROR IN AILocalTraffic::GetNextTaxiNode - two non-nodes in sequence\n");
+                               SG_LOG(SG_ATC, SG_ALERT, "ERROR IN AILocalTraffic::GetNextTaxiNode - two non-nodes in sequence\n");
                        }
                }
        }
@@ -882,11 +1243,11 @@ void FGAILocalTraffic::Taxi(double dt) {
                double slope = 0.0;
                pos = dclUpdatePosition(pos, track, slope, dist);
                //cout << "Updated position...\n";
-               if(aip.getFGLocation()->get_cur_elev_m() > -9990) {
-                       pos.setelev(aip.getFGLocation()->get_cur_elev_m() + wheelOffset);
+               if(aip.getSGLocation()->get_cur_elev_m() > -9990) {
+                       pos.setelev(aip.getSGLocation()->get_cur_elev_m() + wheelOffset);
                } // else don't change the elev until we get a valid ground elev again!
        } else if(lastNode) {
-               if(taxiState == TD_OUTBOUND) {
+               if(taxiState == TD_LINING_UP) {
                        if((!liningUp) && (dist_to_go <= taxiTurnRadius)) {
                                liningUp = true;
                        }
@@ -901,8 +1262,8 @@ void FGAILocalTraffic::Taxi(double dt) {
                                double slope = 0.0;
                                pos = dclUpdatePosition(pos, track, slope, dist);
                                //cout << "Updated position...\n";
-                               if(aip.getFGLocation()->get_cur_elev_m() > -9990) {
-                                       pos.setelev(aip.getFGLocation()->get_cur_elev_m() + wheelOffset);
+                               if(aip.getSGLocation()->get_cur_elev_m() > -9990) {
+                                       pos.setelev(aip.getSGLocation()->get_cur_elev_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;
@@ -911,6 +1272,10 @@ void FGAILocalTraffic::Taxi(double dt) {
                                        liningUp = false;
                                }
                        }
+               } else if(taxiState == TD_OUTBOUND) {
+                       // Pause awaiting further instructions
+                       // and for now assume we've reached the hold-short node
+                       holdingShort = true;
                } // else at the moment assume TD_INBOUND always ends in a gate in which case we can ignore it
        } else {
                // Time to turn (we've already checked it's not the end we're heading for).
@@ -932,13 +1297,14 @@ void FGAILocalTraffic::DoGroundElev() {
        // 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.getFGLocation()->set_tile_center(Point3D(buck.get_center_lon(), buck.get_center_lat(), 0.0));
+       //aip.getSGLocation()->set_tile_center(Point3D(buck.get_center_lon(), buck.get_center_lat(), 0.0));
        
        double visibility_meters = fgGetDouble("/environment/visibility-m");
        //globals->get_tile_mgr()->prep_ssg_nodes( acmodel_location,
-       globals->get_tile_mgr()->prep_ssg_nodes( aip.getFGLocation(),   visibility_meters );
-       globals->get_tile_mgr()->update( aip.getFGLocation(), visibility_meters, (aip.getFGLocation())->get_absolute_view_pos() );
-       // save results of update in FGLocation for fdm...
+       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->
@@ -946,9 +1312,10 @@ void FGAILocalTraffic::DoGroundElev() {
        //}
        
        // The need for this here means that at least 2 consecutive passes are needed :-(
-       aip.getFGLocation()->set_tile_center( globals->get_scenery()->get_next_center() );
+       aip.getSGLocation()->set_tile_center( globals->get_scenery()->get_next_center() );
        
        //cout << "Transform Elev is " << globals->get_scenery()->get_cur_elev() << '\n';
-       aip.getFGLocation()->set_cur_elev_m(globals->get_scenery()->get_cur_elev());
+       aip.getSGLocation()->set_cur_elev_m(globals->get_scenery()->get_cur_elev());
        //return(globals->get_scenery()->get_cur_elev());
 }
+