diff --git a/src/ATC/AILocalTraffic.cxx b/src/ATC/AILocalTraffic.cxx
index a2757598f..f4dc8dbea 100644
--- a/src/ATC/AILocalTraffic.cxx
+++ b/src/ATC/AILocalTraffic.cxx
@@ -23,6 +23,7 @@
 #  include <config.h>
 #endif
 
+#include <Airports/runways.hxx>
 #include <Main/globals.hxx>
 #include <Main/location.hxx>
 #include <Scenery/scenery.hxx>
@@ -40,6 +41,10 @@ SG_USING_STD(string);
 #include "ATCutils.hxx"
 
 FGAILocalTraffic::FGAILocalTraffic() {
+	roll = 0.0;
+	pitch = 0.0;
+	hdg = 270.0;
+	
 	//Hardwire initialisation for now - a lot of this should be read in from config eventually
 	Vr = 70.0;
 	best_rate_of_climb_speed = 70.0;
@@ -56,31 +61,105 @@ FGAILocalTraffic::FGAILocalTraffic() {
 	nominalTaxiSpeed = 8.0;
 	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);
 	circuitsToFly = 0;
+	liningUp = false;
 }
 
 FGAILocalTraffic::~FGAILocalTraffic() {
 }
 
-void FGAILocalTraffic::Init() {
+
+// 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.
+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() );
+	
+	//wind
+	double hdg = wind_from_hdg->getDoubleValue();
+	double speed = wind_speed_knots->getDoubleValue();
+	hdg = (speed == 0.0 ? 270.0 : hdg);
+	//cout << "Heading = " << hdg << '\n';
+	
+	FGRunway runway;
+	bool rwyGood = runways.search(airportID, int(hdg), &runway);
+	if(rwyGood) {
+		// Get the threshold position
+    	hdg = runway.heading;
+		//cout << "hdg reset to " << hdg << '\n';
+		double other_way = hdg - 180.0;
+		while(other_way <= 0.0) {
+			other_way += 360.0;
+		}
+
+    	// move to the +l end/center of the runway
+		//cout << "Runway center is at " << runway.lon << ", " << runway.lat << '\n';
+    	Point3D origin = Point3D(runway.lon, runway.lat, aptElev);
+		Point3D ref = origin;
+    	double tshlon, tshlat, tshr;
+		double tolon, tolat, tor;
+		rwy.length = runway.length * SG_FEET_TO_METER;
+    	geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), other_way, 
+        	                rwy.length / 2.0 - 25.0, &tshlat, &tshlon, &tshr );
+    	geo_direct_wgs_84 ( aptElev, ref.lat(), ref.lon(), hdg, 
+        	                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);
+		//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");
+	}
+}
+
+/* 
+There are two possible scenarios during initialisation:
+The first is that the user is flying towards the airport, and hence the traffic
+could be initialised anywhere, as long as the AI planes are consistent with
+each other.
+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.
+*/
+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());
-	aip.setVisible(true);
+	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());
-	// is it OK to leave it like this until the first time transform is called?
-	// Really ought to be started in a parking space unless otherwise specified?
 	
 	// Find the tower frequency - this is dependent on the ATC system being initialised before the AI system
-	// FIXME - ATM this is hardwired.
-	airportID = "KEMT";
+	airportID = ICAO;
 	AirportATC a;
-	if(globals->get_ATC_mgr()->GetAirportATCDetails((string)airportID, &a)) {
+	if(globals->get_ATC_mgr()->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
 			freq = (double)tower->get_freq() / 100.0;
@@ -92,94 +171,220 @@ void FGAILocalTraffic::Init() {
 		//cout << "Unable to find airport details in FGAILocalTraffic::Init()\n";
 	}
 
-	// Initiallise the FGAirportData structure
+	// 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 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';
+	operatingState = initialState;
+	switch(operatingState) {
+	case PARKED:
+		ourGate = airport.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');
+			return(false);
+		}
+		pitch = 0.0;
+		roll = 0.0;
+		vel = 0.0;
+		slope = 0.0;
+		pos = ourGate->pos;
+		pos.setelev(aptElev);
+		hdg = ourGate->heading;
+		
+		// Now we've set the position we can do the ground elev
+		elevInitGood = false;
+		inAir = false;
+		DoGroundElev();
+		
+		Transform();
+		break;
+	case TAXIING:
+		// FIXME - implement this case properly
+		return(false);	// remove this line when fixed!
+		break;
+	case IN_PATTERN:
+		// For now we'll always start the in_pattern case on the threshold ready to take-off
+		// 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;
+		
+		// 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;
+		
+		circuitsToFly = 0;		// ie just fly this circuit and then stop
+		touchAndGo = false;
+		// 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);
+		}
+		
+		operatingState = IN_PATTERN;
+		
+		Transform();
+		break;
+	default:
+		SG_LOG(SG_GENERAL, SG_ALERT, "Attempt to set unknown operating state in FGAILocalTraffic.Init(...)\n");
+		return(false);
+	}
+	
+	
+	return(true);
 }
 
 // Commands to do something from higher level logic
 void FGAILocalTraffic::FlyCircuits(int numCircuits, bool tag) {
-	circuitsToFly += numCircuits - 1;	// Hack (-1) because we only test and decrement circuitsToFly after landing
-										// thus flying one to many circuits.  TODO - Need to sort this out better!
-	touchAndGo = tag;
+	//cout << "FlyCircuits called" << endl;
 	
-	//At the moment we'll assume that we are always finished previous circuits when called,
-	//And just teleport to the threshold to start.
-	//This is a hack though, we need to check where we are and taxi out if appropriate.
-	operatingState = IN_PATTERN;
+	switch(operatingState) {
+	case IN_PATTERN:
+		circuitsToFly += numCircuits;
+		return;
+		break;
+	case TAXIING:
+		// 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
+										// 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);
+		}
 
-	// Hardwire to KEMT for now
-	// Hardwired points at each end of KEMT runway
-	Point3D P010(-118.037483, 34.081358, 296 * SG_FEET_TO_METER);
-	Point3D P190(-118.032308, 34.090456, 299.395263 * SG_FEET_TO_METER);
-	Point3D takeoff_end;
-	bool d010 = true;	// use this to change the hardwired runway direction
-	if(d010) {
-		rwy.threshold_pos = P010;
-		takeoff_end = P190;
-		rwy.hdg = 25.32;	//from default.apt
-		rwy.ID = 1;
-		patternDirection = -1;	// Left
-		pos.setelev(rwy.threshold_pos.elev() + (-8.5 * SG_FEET_TO_METER));  // This is a complete hack - the rendered runway takes the underlying scenery elev rather than the published runway elev so I should use height above terrain or something.
-	} else {
-		rwy.threshold_pos = P190;
-		takeoff_end = P010;
-		rwy.hdg = 205.32;
-		rwy.ID = 19;
-		patternDirection = 1;	// Right
-		pos.setelev(rwy.threshold_pos.elev() + (-0.0 * SG_FEET_TO_METER));  // This is a complete hack - the rendered runway takes the underlying scenery elev rather than the published runway elev so I should use height above terrain or something.
+		Transform();
+		break;
 	}
-	
-	//rwy.threshold_pos.setlat(34.081358);
-	//rwy.threshold_pos.setlon(-118.037483);
-	//rwy.mag_hdg = 12.0;
-	//rwy.mag_var = 14.0;
-	//rwy.hdg = rwy.mag_hdg + rwy.mag_var;
-	//rwy.threshold_pos.setelev(296 * SG_FEET_TO_METER);
-	
-	// Initial position on threshold for now
-	// TODO - check wind / default runway
-	pos.setlat(rwy.threshold_pos.lat());
-	pos.setlon(rwy.threshold_pos.lon());
-	hdg = rwy.hdg;
-	
-	pitch = 0.0;
-	roll = 0.0;
-	leg = TAKEOFF_ROLL;
-	vel = 0.0;
-	slope = 0.0;
-	
-	// 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
-	// Hardwire to KEMT for now
-	rwy.end2ortho = ortho.ConvertToLocal(takeoff_end);
-	//cout << "*********************************************************************************\n";
-	//cout << "*********************************************************************************\n";
-	//cout << "*********************************************************************************\n";
-	//cout << "end1ortho = " << rwy.end1ortho << '\n';
-	//cout << "end2ortho = " << rwy.end2ortho << '\n';	// end2ortho.x() should be zero or thereabouts
-	
-	Transform();
 }   
 
 // Run the internal calculations
 void FGAILocalTraffic::Update(double dt) {
 	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;
+			}
+		}
 		FlyTrafficPattern(dt);
 		Transform();
 		break;
 	case TAXIING:
+		//cout << "In TAXIING\n";
+		if(!elevInitGood) {
+			//DoGroundElev();
+			if(aip.getFGLocation()->get_cur_elev_m() > -9990.0) {
+				pos.setelev(aip.getFGLocation()->get_cur_elev_m() + wheelOffset);
+				//Transform();
+				aip.setVisible(true);
+				//Transform();
+				//cout << "Making plane visible!\n";
+				elevInitGood = true;
+			}
+		}
 		DoGroundElev();
 		Taxi(dt);
 		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);
+				//Transform();
+				aip.setVisible(true);
+				//Transform();
+				//cout << "Making plane visible!\n";
+				elevInitGood = true;
+			}
+		}
 		// Do nothing
+		Transform();
 		break;
 	default:
 		break;
@@ -192,7 +397,6 @@ void FGAILocalTraffic::Update(double dt) {
 void FGAILocalTraffic::FlyTrafficPattern(double dt) {
 	// Need to differentiate between in-air (IAS governed) and on-ground (vel governed)
 	// Take-off is an interesting case - we are on the ground but takeoff speed is IAS governed.
-	bool inAir = true;	// FIXME - possibly make into a class variable
 	
 	static bool transmitted = false;	// FIXME - this is a hack
 
@@ -222,18 +426,22 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
 
 	switch(leg) {
 	case TAKEOFF_ROLL:
-		inAir = false;
+		//inAir = false;
 		track = rwy.hdg;
 		if(vel < 80.0) {
 			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);
+		}
 		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;
+			inAir = true;
 		}
 		break;
 	case CLIMBOUT:
@@ -348,15 +556,26 @@ void FGAILocalTraffic::FlyTrafficPattern(double dt) {
 		// Try and track the extended centreline
 		track = rwy.hdg - (0.2 * orthopos.x());
 		//cout << "orthopos.x() = " << orthopos.x() << " hdg = " << hdg << '\n';
-		if(pos.elev() <= rwy.threshold_pos.elev()) {
-			pos.setelev(rwy.threshold_pos.elev());// + (-8.5 * SG_FEET_TO_METER));  // This is a complete hack - the rendered runway takes the underlying scenery elev rather than the published runway elev so I should use height above terrain or something.
-			slope = 0.0;
-			pitch = 0.0;
-			leg = LANDING_ROLL;
+		if(pos.elev() < (rwy.threshold_pos.elev()+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(aip.getFGLocation()->get_cur_elev_m() > -9990.0) {
+				if((aip.getFGLocation()->get_cur_elev_m() + wheelOffset) > pos.elev()) {
+					slope = 0.0;
+					pitch = 0.0;
+					leg = LANDING_ROLL;
+					inAir = false;
+				}
+			}	// else need a fallback position based on arpt elev in case ground elev determination fails?
 		}
 		break;
 	case LANDING_ROLL:
-		inAir = false;
+		//inAir = false;
+		if(aip.getFGLocation()->get_cur_elev_m() > -9990.0) {
+			pos.setelev(aip.getFGLocation()->get_cur_elev_m() + wheelOffset);
+		}
 		track = rwy.hdg;
 		double dveldt = -5.0;
 		vel += dveldt * dt;
@@ -506,8 +725,8 @@ void FGAILocalTraffic::ExitRunway(Point3D orthopos) {
 			}
 			++nItr;
 		}
-		in_dest = airport.GetGateNode();
-		if(in_dest == NULL) {
+		ourGate = airport.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');
@@ -515,7 +734,7 @@ void FGAILocalTraffic::ExitRunway(Point3D orthopos) {
 			operatingState = PARKED;
 			return;
 		}
-		path = airport.GetPath(rwyExit, in_dest);
+		path = airport.GetPath(rwyExit, ourGate);
 		/*
 		cout << "path returned was:" << endl;
 		for(unsigned int i=0; i<path.size(); ++i) {
@@ -590,6 +809,40 @@ void FGAILocalTraffic::StartTaxi() {
 	//cout << "First taxi heading is " << desiredTaxiHeading << endl;
 }
 
+// speed in knots, headings in degrees, radius in meters.
+static double TaxiTurnTowardsHeading(double current_hdg, double desired_hdg, double speed, double radius, double dt) {
+	// wrap heading - this prevents a logic bug where the plane would just go round in circles!!
+	while(current_hdg < 0.0) {
+		current_hdg += 360.0;
+	}
+	while(current_hdg > 360.0) {
+		current_hdg -= 360.0;
+	}
+	if(fabs(current_hdg - desired_hdg) > 0.1) {
+		// Which is the quickest direction to turn onto heading?
+		if(desired_hdg > current_hdg) {
+			if((desired_hdg - current_hdg) <= 180) {
+				// turn right
+				current_hdg += ((speed * 0.514444 * dt) / (radius * DCL_PI)) * 180.0;
+				// TODO - check that increments are less than the delta that we check for the right direction
+				// Probably need to reduce convergence speed as convergence is reached
+			} else {
+				current_hdg -= ((speed * 0.514444 * dt) / (radius * DCL_PI)) * 180.0; 	
+			}
+		} else {
+			if((current_hdg - desired_hdg) <= 180) {
+				// turn left
+				current_hdg -= ((speed * 0.514444 * dt) / (radius * DCL_PI)) * 180.0;
+				// TODO - check that increments are less than the delta that we check for the right direction
+				// Probably need to reduce convergence speed as convergence is reached
+			} else {
+				current_hdg += ((speed * 0.514444 * dt) / (radius * DCL_PI)) * 180.0; 	
+			}
+		}	        
+	}
+	return(current_hdg);
+}
+
 void FGAILocalTraffic::Taxi(double dt) {
 	//cout << "Taxi called" << endl;
 	// Logic - if we are further away from next point than turn radius then head for it
@@ -598,6 +851,11 @@ void FGAILocalTraffic::Taxi(double dt) {
 
 	//Point3D orthopos = ortho.ConvertToLocal(pos);	// ortho position of the plane
 	desiredTaxiHeading = GetHeadingFromTo(pos, nextTaxiNode->pos);
+	
+	bool lastNode = (taxiPathPos == path.size() ? true : false);
+	if(lastNode) {
+		//cout << "LAST NODE\n";
+	}
 
 	// HACK ALERT! - for now we will taxi at constant speed for straights and turns
 	
@@ -605,37 +863,16 @@ void FGAILocalTraffic::Taxi(double dt) {
 	double dist_to_go = dclGetHorizontalSeparation(pos, nextTaxiNode->pos);	// we may be able to do this more cheaply using orthopos
 	//cout << "dist_to_go = " << dist_to_go << endl;
 	if((nextTaxiNode->type == GATE) && (dist_to_go <= 0.1)) {
+		// This might be more robust to outward paths starting with a gate if we check for either
+		// last node or TD_INBOUND ?
 		// park up
-		//taxiing = false;
-		//parked = true;
 		operatingState = PARKED;
-	} else if((dist_to_go > taxiTurnRadius) || (nextTaxiNode->type == GATE)) {
+	} else if(((dist_to_go > taxiTurnRadius) || (nextTaxiNode->type == GATE)) && (!liningUp)){
 		// if the turn radius is r, and speed is s, then in a time dt we turn through
 		// ((s.dt)/(PI.r)) x 180 degrees
 		// or alternatively (s.dt)/r radians
 		//cout << "hdg = " << hdg << " desired taxi heading = " << desiredTaxiHeading << '\n';
-		if(fabs(hdg - desiredTaxiHeading) > 0.1) {
-			// Which is the quickest direction to turn onto heading?
-			if(desiredTaxiHeading > hdg) {
-				if((desiredTaxiHeading - hdg) <= 180) {
-					// turn right
-					hdg += ((nominalTaxiSpeed * 0.514444 * dt) / (taxiTurnRadius * DCL_PI)) * 180.0;
-					// TODO - check that increments are less than the delta that we check for the right direction
-					// Probably need to reduce convergence speed as convergence is reached
-				} else {
-					hdg -= ((nominalTaxiSpeed * 0.514444 * dt) / (taxiTurnRadius * DCL_PI)) * 180.0; 	
-				}
-			} else {
-				if((hdg - desiredTaxiHeading) <= 180) {
-					// turn left
-					hdg -= ((nominalTaxiSpeed * 0.514444 * dt) / (taxiTurnRadius * DCL_PI)) * 180.0;
-					// TODO - check that increments are less than the delta that we check for the right direction
-					// Probably need to reduce convergence speed as convergence is reached
-				} else {
-					hdg += ((nominalTaxiSpeed * 0.514444 * dt) / (taxiTurnRadius * DCL_PI)) * 180.0; 	
-				}
-			}	        
-		}
+		hdg = TaxiTurnTowardsHeading(hdg, desiredTaxiHeading, nominalTaxiSpeed, taxiTurnRadius, dt);
 		double vel = nominalTaxiSpeed;
 		//cout << "vel = " << vel << endl;
 		double dist = vel * 0.514444 * dt;
@@ -648,6 +885,33 @@ void FGAILocalTraffic::Taxi(double dt) {
 		if(aip.getFGLocation()->get_cur_elev_m() > -9990) {
 			pos.setelev(aip.getFGLocation()->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((!liningUp) && (dist_to_go <= taxiTurnRadius)) {
+				liningUp = true;
+			}
+			if(liningUp) {
+				hdg = TaxiTurnTowardsHeading(hdg, rwy.hdg, nominalTaxiSpeed, taxiTurnRadius, dt);
+				double vel = nominalTaxiSpeed;
+				//cout << "vel = " << vel << endl;
+				double dist = vel * 0.514444 * dt;
+				//cout << "dist = " << dist << endl;
+				double track = hdg;
+				//cout << "track = " << track << endl;
+				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);
+				} // else don't change the elev until we get a valid ground elev again!
+				if(fabs(hdg - rwy.hdg) <= 1.0) {
+					operatingState = IN_PATTERN;
+					leg = TAKEOFF_ROLL;
+					inAir = false;
+					liningUp = false;
+				}
+			}
+		} // 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).
 		// set the target node to be the next node which will prompt automatically turning onto
diff --git a/src/ATC/AILocalTraffic.hxx b/src/ATC/AILocalTraffic.hxx
index 12373988d..e7b00d269 100644
--- a/src/ATC/AILocalTraffic.hxx
+++ b/src/ATC/AILocalTraffic.hxx
@@ -19,14 +19,6 @@
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-/*****************************************************************
-*
-* WARNING - Curt has some ideas about AI traffic so anything in here
-* may get rewritten or scrapped.  Contact Curt curt@flightgear.org 
-* before spending any time or effort on this code!!!
-*
-******************************************************************/
-
 #ifndef _FG_AILocalTraffic_HXX
 #define _FG_AILocalTraffic_HXX
 
@@ -40,6 +32,9 @@
 #include "ATCProjection.hxx"
 #include "ground.hxx"
 
+#include <string>
+SG_USING_STD(string);
+
 typedef enum PatternLeg {
 	TAKEOFF_ROLL,
 	CLIMBOUT,
@@ -74,7 +69,9 @@ typedef struct RunwayDetails {
 	double mag_hdg;
 	double mag_var;
 	double hdg;		// true runway heading
+	double length;	// In *METERS*
 	int ID;		// 1 -> 36
+	string rwyID;
 };
 
 typedef struct StartofDescent {
@@ -91,7 +88,7 @@ public:
 	~FGAILocalTraffic();
 	
 	// Initialise
-	void Init();
+	bool Init(string ICAO, OperatingState initialState = PARKED, PatternLeg initialLeg = DOWNWIND);
 	
 	// Run the internal calculations
 	void Update(double dt);
@@ -118,7 +115,8 @@ private:
 	// and the runway aligned with the y axis.
 	
 	// Airport/runway/pattern details
-	char* airportID;	// The ICAO code of the airport that we're operating around
+	string airportID;	// The ICAO code of the airport that we're operating around
+	double aptElev;		// Airport elevation
 	FGGround airport;	// FIXME FIXME FIXME This is a complete hardwired cop-out at the moment - we need to connect to the correct ground in the same way we do to the tower.
 	FGTower* tower;	// A pointer to the tower control.
 	RunwayDetails rwy;
@@ -144,6 +142,8 @@ private:
 	
 	// Physical/rendering stuff
 	double wheelOffset;		// Height above ground at which we need to render the plane whilst taxiing
+	bool elevInitGood;		// We have had at least one good elev reading
+	bool inAir;				// True when off the ground 
 	
 	// environment - some of this might get moved into FGAIPlane
 	SGPropertyNode* wind_from_hdg;	//degrees
@@ -165,11 +165,12 @@ private:
 	double desiredTaxiHeading;
 	double taxiTurnRadius;
 	double nominalTaxiSpeed;
-	Gate* in_dest;
+	Gate* ourGate;
 	ground_network_path_type path;	// a path through the ground network for the plane to taxi
-	int taxiPathPos;	// position of iterator in taxi path when applicable
+	unsigned int taxiPathPos;	// position of iterator in taxi path when applicable
 	node* nextTaxiNode;	// next node in taxi path
 	//Runway out_dest; //FIXME - implement this
+	bool liningUp;	// Set true when the turn onto the runway heading is commenced when taxiing out
 
 	void FlyTrafficPattern(double dt);
 
@@ -188,6 +189,8 @@ private:
 	void GetNextTaxiNode();
 	
 	void DoGroundElev();
+	
+	void GetRwyDetails();
 };
 
 #endif  // _FG_AILocalTraffic_HXX
diff --git a/src/ATC/AIMgr.cxx b/src/ATC/AIMgr.cxx
index 5dd7bbee4..85ec94783 100644
--- a/src/ATC/AIMgr.cxx
+++ b/src/ATC/AIMgr.cxx
@@ -19,16 +19,6 @@
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-/*****************************************************************
-*
-* WARNING - Curt has some ideas about AI traffic so anything in here
-* may get rewritten or scrapped.  Contact Curt curt@flightgear.org 
-* before spending any time or effort on this code!!!
-*
-******************************************************************/
-
-
-
 #include <Main/fgfs.hxx>
 #include <Main/fg_props.hxx>
 
@@ -50,7 +40,8 @@ void FGAIMgr::init() {
 	// Hard wire some local traffic for now.
 	// This is regardless of location and hence *very* ugly but it is a start.
 	FGAILocalTraffic* local_traffic = new FGAILocalTraffic;
-	local_traffic->Init();
+	//local_traffic->Init("KEMT", IN_PATTERN, TAKEOFF_ROLL);
+	local_traffic->Init("KEMT");
 	local_traffic->FlyCircuits(1, true);	// Fly 2 circuits with touch & go in between
 	ai_list.push_back(local_traffic);
 }
@@ -64,12 +55,15 @@ void FGAIMgr::unbind() {
 void FGAIMgr::update(double dt) {
 	// Don't update any planes for first 50 runs through - this avoids some possible initialisation anomalies
 	static int i = 0;
-	i++;
 	if(i < 50) {
+		i++;
 		return;
 	}
 	
 	// Traverse the list of active planes and run all their update methods
+	// TODO - spread the load - not all planes should need updating every frame.
+	// Note that this will require dt to be calculated for each plane though
+	// since they rely on it to calculate distance travelled.
 	ai_list_itr = ai_list.begin();
 	while(ai_list_itr != ai_list.end()) {
 		(*ai_list_itr)->Update(dt);
diff --git a/src/ATC/AIMgr.hxx b/src/ATC/AIMgr.hxx
index 35276aeae..ec4c4e40c 100644
--- a/src/ATC/AIMgr.hxx
+++ b/src/ATC/AIMgr.hxx
@@ -19,14 +19,6 @@
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-/*****************************************************************
-*
-* WARNING - Curt has some ideas about AI traffic so anything in here
-* may get rewritten or scrapped.  Contact Curt curt@flightgear.org 
-* before spending any time or effort on this code!!!
-*
-******************************************************************/
-
 #ifndef _FG_AIMGR_HXX
 #define _FG_AIMGR_HXX