function Plane(arraynum,linecolour,URL) {

	var me = this;
	//me.waitingForData = true;
	
	me.glidernumber=arraynum;
	gliders[arraynum] = me;

    window.google.earth.fetchKml(ge, URL, function(obj) { me.finishInit(obj,arraynum,linecolour); });
}


Plane.prototype.finishInit = function(kml,arraynum,linecolour) {

    // setting up the glider once the KML file has loaded

	var me = this;

	me.glidernumber=arraynum;
	me.oldlat2=0;
	me.oldlon2=0;
	me.newlat=0;
	  me.oldlat=0;
	  me.newlon=0;
	  me.oldlon=0;
	  me.newlat2=0;
	  me.newlon2=0;
	  me.newlat3=0;
	  me.newlon3=0;
	  me.newlat4=0;
	  me.newlon4=0;
	  me.oldalt=0;
	  me.newalt=0;
	  me.newalt2=0;
	  me.newalt3=0;
	  me.newalt4=0;
	  me.roll=0;
	  me.missing=0;
	  me.oldtimecode=0;
	  me.lasttimecode=0;
	  me.futuretimecode=10000000000000000000000;
	  me.updateinterval=4000;
	  me.movement=0;
	  me.oldmovement=0;


	  me.rollleft='no';
	  me.rollright='no';
	  me.groundalt=0;
	
	  me.degrees=0;
	  me.degrees2=0;
	  me.olddegrees=0;
	  me.olderdegrees=0;
	  me.curdegrees=0;
	  me.oldcurdegrees=0;
	  me.prevdegrees=0;
	  me.initialheadingset=false;
	
	  me.prevlat=0;
	  me.prevlon=0;
	  me.prevprevlat=0;
	  me.prevprevlon=0;  

	  me.n=0;
	  me.f=0;
	  me.d=0;
	  me.c=0;
	  me.s=0;
	  me.r=0;
	  me.a=0;
 
	  me.curtime = (new Date()).getTime();
	  me.lasttime = (new Date()).getTime();
	  me.passedtime = 10000; // so we get a new position straight away
	  me.nextdistance = 100;
	  me.distance=0;
	  me.placemark = kml.getFeatures().getChildNodes().item(1);
	  me.model = me.placemark.getGeometry();
	  me.orientation = me.model.getOrientation();
	  me.location = me.model.getLocation();
	  me.direction=0;
	  me.climbicon="";
	  me.climbdiff=0;
	  me.position=15;
	  me.lastposition=15;


	  me.orientation.setHeading(90);
	  me.model.setOrientation(me.orientation);
	
	  var camera = ge.getView().copyAsCamera(ge.ALTITUDE_RELATIVE_TO_GROUND);
	
	  me.newlat=camera.getLatitude();
	  me.newlat2=camera.getLatitude();
	  me.newlat3=camera.getLatitude();
	  me.newlat4=camera.getLatitude();
	
	  me.newalt=camera.getAltitude();
	  me.newalt2=camera.getAltitude();
	  me.newalt3=camera.getAltitude();
	  me.newalt4=camera.getAltitude();
	
	  me.oldlat=camera.getLatitude();
	  me.oldalt=camera.getAltitude();
	
	  me.newlon=camera.getLongitude();
	  me.newlon2=camera.getLongitude();
	  me.newlon3=camera.getLongitude();
	  me.newlon4=camera.getLongitude();
	  
	  me.oldlon=camera.getLongitude();
	
	  me.model.setAltitudeMode(ge.ALTITUDE_ABSOLUTE);
	
	  var loc = ge.createLocation('');
	  loc.setLatitude(camera.getLatitude());
	  loc.setLongitude(camera.getLongitude());
	  loc.setAltitude(camera.getAltitude());
	  me.model.setLocation(loc);
	
	  ge.getFeatures().appendChild(me.placemark);
	
	  // put glider object model into the array for future reference
	  gliders[arraynum] = me;
	
	  // Create the placemark
	  me.lineStringPlacemark = ge.createPlacemark('');
		
		// Create the LineString; set it to extend down to the ground
		// and set the altitude mode
		me.lineString = ge.createLineString('');
		me.lineStringPlacemark.setGeometry(me.lineString);
		me.lineString.setExtrude(false);
		me.lineString.setAltitudeMode(ge.ALTITUDE_ABSOLUTE);
		
		// Create a style and set width and color of line
		me.lineStringPlacemark.setStyleSelector(ge.createStyle(''));
		me.lineStyle = me.lineStringPlacemark.getStyleSelector().getLineStyle();
		me.lineStyle.setWidth(3);
		me.lineStyle.getColor().set(linecolour);  // aabbggrr format
		
		// Add the feature to Earth
		
		ge.getFeatures().appendChild(me.lineStringPlacemark);
		pilotTrail[me.glidernumber]=me.lineString;
		
	  // start animating the glider
	  google.earth.addEventListener(ge, "frameend", function() { me.tickAnimation(); });
	
	  ge.getView().setAbstractView(la);
	
}
  



Plane.prototype.tickAnimation = function() {
	me=this;

	
    // Glider animation routine (making up new positions currently)
    var camera = ge.getView().copyAsCamera(ge.ALTITUDE_ABSOLUTE);
    me.curtime = (new Date()).getTime();
    me.timediff = me.curtime-me.lasttime;
    me.passedtime=me.passedtime+me.timediff;
 
    // set new lat/lon if we have gone over our allotted time (updateinterval)

    if (me.passedtime > me.updateinterval) {

        me.passedtime=0;

        me.oldlat=me.model.getLocation().getLatitude(); // move current to old
        me.oldlon=me.model.getLocation().getLongitude(); // move current to old
        me.oldalt=me.model.getLocation().getAltitude(); // move current to old

		/*
		if (Math.round(me.oldalt) < 10000 && Math.round(me.nextdistance/1000*12*60) < 500) {
				document.getElementById('spd_'+me.glidernumber).innerHTML=Math.round(me.nextdistance/1000*12*60)+"kph";
				document.getElementById('alt_'+me.glidernumber).innerHTML=Math.round(me.oldalt)+" ("+Math.round(me.groundalt)+") m";
				document.getElementById('rate_'+me.glidernumber).innerHTML=Math.round((climbdiff/5)*10)/10+"&nbsp;<img src='"+climbicon+"'>";
		}
*/

		
		var now = (new Date() - (60*1000*playbackDelay)) + tcf;
		var fixes = data[me.glidernumber];
		
		//if (fixes.length > 1) me.waitingForData = false;
		
		
		var discardcount=0;
		toPlot=null;
		toPlot2=null;
		toPlot3=null;
		toPlot4=null;

		for (var j = 0, len = fixes.length; j < len; ++j) {

			if (fixes[j].t > now) {
				toPlot2 = fixes[j];
				toPlot3 = fixes[j+1];
				toPlot4 = fixes[j+2];
				break;
			}
			 
			toPlot = fixes[j];
			toPlot2 = fixes[j];
			toPlot3 = fixes[j];
			toPlot4 = fixes[j];
	
		}
		
						
		if (toPlot) {
			me.newlat = toPlot.x;
			me.newlon = toPlot.y;
			me.newalt = toPlot.z;
			
			//logger(toPlot.n+"  "+toPlot.d+"   "+toPlot.f+"   "+toPlot.c);
			
			if (me.newalt > 0) {
				me.n=toPlot.n;
				me.d=toPlot.d;
				me.f=toPlot.f;
				me.c=toPlot.c;
				me.s=toPlot.s;
				me.r=toPlot.r;
				me.a=toPlot.a;
			}
			
		}

		if (toPlot2) {
			me.newlat2=toPlot2.x;
			me.newlon2=toPlot2.y;
			me.newalt2=toPlot2.z;
		}
		
		if (toPlot3) {
			me.newlat3=toPlot3.x;
			me.newlon3=toPlot3.y;
			me.newalt3=toPlot3.z;
		}

		if (toPlot4) {
			me.newlat4=toPlot4.x;
			me.newlon4=toPlot4.y;
			me.newalt4=toPlot4.z;
		}

		me.olderdegrees=me.olddegrees;
        me.olddegrees = me.degrees;

		me.updateinterval=toPlot2.t - toPlot.t;
		
		
	
		var groundAlt=0;	
			
			// check the glider location is not below the ground
		try {
			groundAlt = ge.getGlobe().getGroundAltitude(me.newlat, me.newlon);
		} catch (err){}
		
		
		if (groundAlt) {
			if (me.newalt <= groundAlt) {
			  me.newalt = groundAlt+5;
			}
		}
			
		
	    var a = Math.sin(me.oldlat * Math.PI / 180) * Math.sin(me.newlat * Math.PI / 180);
	    var b = Math.cos(me.oldlat * Math.PI / 180) * Math.cos(me.newlat * Math.PI / 180) * Math.cos((me.newlon - me.oldlon) * Math.PI / 180);
	    me.nextdistance= 6371000 * Math.acos(a + b);
	
		
		if (me.updateinterval > 15000) {me.updateinterval=15000;}
		if (me.nextdistance > 1000) {me.updateinterval=500;}
	
		me.degrees=me.degrees2;
	
	// now work out angles2
	
	    var y2 = Math.sin(me.newlon-me.newlon2) * Math.cos(me.newlat2);
	    var x2 = Math.cos(me.newlat)*Math.sin(me.newlat2) - Math.sin(me.newlat)*Math.cos(me.newlat2)*Math.cos(me.newlon-me.newlon2);
	
	// change new heading(2) for glider from radians to degrees and put into glider
	
		  me.degrees2 = fixAngle(Math.atan2(-y2, x2) * 57.2957795);
		  me.degrees = fixAngle(me.degrees);
		
	// setup the client in the right direction just in case we missed a beat
	
	//var orientation = me.model.getOrientation();
	//orientation.setHeading(me.degrees);
	//me.model.setOrientation(orientation);
	
	// which way is quickest - clockwise or anti-clockwise?
		var clockwise = fixAngle(me.degrees2-me.degrees);
		var anticlockwise = fixAngle(me.degrees-me.degrees2);

		me.oldmovement=me.movement;
		
		var showdirection = "";
		
		if (clockwise < anticlockwise) {me.movement=clockwise;showdirection="CLOCKWISE";} else {me.movement=anticlockwise*-1;showdirection="ANTICLOCK";}
		
	 }


// this bit happens *every* tick, rather than once every 5 seconds as per the above
// divide up the distance we have to travel into piece size chunks based on how much time has passed this time (timediff)

    me.distance=me.nextdistance/(me.updateinterval/me.timediff);
	
	var lat=me.oldlat+((me.newlat-me.oldlat)/(me.updateinterval/me.passedtime));
    var lon=me.oldlon+((me.newlon-me.oldlon)/(me.updateinterval/me.passedtime));
	var alt=me.oldalt+((me.newalt-me.oldalt)/(me.updateinterval/me.passedtime));

    var zoldlat=parseFloat(me.oldlat);
    var zoldlon=parseFloat(me.oldlon);

	var controlpoint1lat=parseFloat(me.newlat);
	var controlpoint1lon=parseFloat(me.newlon);
	
	var controlpoint2lat=parseFloat(me.newlat2);
	var controlpoint2lon=parseFloat(me.newlon2);

    var znewlat=parseFloat(me.newlat3);
    var znewlon=parseFloat(me.newlon3);

    var zcontrolpoint1lat=controlpoint1lat;
    var zcontrolpoint1lon=controlpoint1lon;
	
	var zcontrolpoint2lat=controlpoint2lat;
    var zcontrolpoint2lon=controlpoint2lon;


	var u=(me.passedtime/me.updateinterval)/4.05;	

	var bezlat = Math.pow(u,3)*(znewlat+3*(zcontrolpoint1lat-zcontrolpoint2lat)-zoldlat)+3*Math.pow(u,2)*(zoldlat-2*zcontrolpoint1lat+zcontrolpoint2lat)+3*u*(zcontrolpoint1lat-zoldlat)+zoldlat;

	var bezlon = Math.pow(u,3)*(znewlon+3*(zcontrolpoint1lon-zcontrolpoint2lon)-zoldlon)+3*Math.pow(u,2)*(zoldlon-2*zcontrolpoint1lon+zcontrolpoint2lon)+3*u*(zcontrolpoint1lon-zoldlon)+zoldlon;
	
	bezlat=bezlat/1;
	bezlon=bezlon/1;
	
	lat=bezlat;
	lon=bezlon;

	degreesdiffold=(me.olderdegrees-me.olddegrees);
	degreesdiff=(me.olddegrees-me.degrees);
	degreesdiff2=(me.degrees-me.degrees2);
	
	//turning part of this turn
	var thisturnsegment=(me.degrees)-((me.degrees)/(me.updateinterval/me.passedtime));
	
	//turning part of next turn
	var nextturnsegment= ((me.degrees2)/(me.updateinterval/me.passedtime));
	
	// using MOVEMENT variable to calculate which new degrees angle...
	// me.curdegrees=fixAngle(me.degrees+(me.movement/(me.updateinterval/me.passedtime)));
	
	var xlat1 = me.prevlat * Math.PI / 180;
	var xlat2 = lat * Math.PI / 180;
	var xdLon = (lon-me.prevlon) * Math.PI / 180;
	
	var y = Math.sin(xdLon) * Math.cos(xlat2);
	var x = Math.cos(xlat1)*Math.sin(xlat2) - Math.sin(xlat1)*Math.cos(xlat2)*Math.cos(xdLon);
	
	var newcurdegrees=Math.round(fixAngle(Math.atan2(y, x) * 180 / Math.PI));
	
	
	if (me.passedtime/me.updateinterval > 0.025 && me.passedtime/me.updateinterval < 0.975) {
		me.curdegrees= Math.round(fixAngle(Math.atan2(y, x) * 180 / Math.PI));
	}
	
	me.prevlat=lat;
	me.prevlon=lon;
	
	var downalt=(me.oldalt)-(me.oldalt*((me.passedtime)/me.updateinterval));
	var upalt=me.newalt*((me.passedtime)/me.updateinterval);
	
	alt=downalt+upalt;



	//lowering part of this turn
	var downturn=(me.oldmovement)-(me.oldmovement*((me.passedtime)/me.updateinterval));
	
	//rising part of next turn
	var upturn= (me.movement)*((me.passedtime)/me.updateinterval);
	
	// calculate total roll
	me.roll = downturn+upturn;



	var tilt=me.roll/20;
	
	if (tilt > 0) {tilt=tilt*-1;}
	
	// make sure we don't get any acrobatics
	
	var compensatedroll=me.roll;
	
	
	if (compensatedroll > 30) {compensatedroll=30;}
	if (compensatedroll < -30) {compensatedroll=-30;}
	
	//if (me.rollleft == 'yes' && compensatedroll < 0) {compensatedroll=compensatedroll*-1;}
	//if (me.rollright == 'yes' && compensatedroll > 0) {compensatedroll=compensatedroll*-1;}
	
	
	var timecompleted=parseInt((me.passedtime/me.updateinterval)*100);
	
	me.roll=compensatedroll*1;

    var orientation = me.model.getOrientation();
    orientation.setHeading(me.curdegrees);
    orientation.setRoll(me.roll);
	orientation.setTilt(tilt);
    me.model.setOrientation(orientation);

	me.prevdegrees=me.curdegrees;
	
// setup the location to put into the model

// check the glider location is not below the ground
	var groundAlt=0;
	
	try {
		groundAlt = ge.getGlobe().getGroundAltitude(lat, lon);
	} catch (err) {}
	
	if (alt <= groundAlt) {
		alt = alt+5;
	}

    me.loc = ge.createLocation('');
    me.loc.setLatitude(lat);
    me.loc.setLongitude(lon);
    me.loc.setAltitude(alt);

// set the model location

    me.model.setLocation(me.loc);

	// adjust the flag position to match the glider position
	
	//labeloffset=camrange/20;
	var labellatoffset=(camtilt-90)/100000;
	var labellonoffset=(camtilt-90)/100000;
	
	var nicealt=Math.round(alt);
	var mypoint = flags[me.glidernumber].getGeometry();
	mypoint.setLatitude(lat+labellatoffset);
	mypoint.setLongitude(lon+labellonoffset);
	mypoint.setAltitude(alt+labeloffset);
	
	
	me.groundalt=groundAlt;
	
	
	var distancetoground=alt-groundAlt;
	
	var distancetoglider=me.c*1000;
	
	var scalingdistance=1;
	
	// which is smaller, distance to ground or distance to other glider?
	
	if (distancetoground >= distancetoglider) {scalingdistance=distancetoglider;} else {scalingdistance=distancetoground;}
	
	var bestscale = (scalingdistance/50)
	
	//if (bestscale > 2) {bestscale = 2;}
	if (bestscale < 1) {bestscale = 1;}
	
	bestscale=bestscale*aircraftscale;
	
	//if (cameratype == "FIXEDFOLLOW" || cameratype == "FOLLOW") {camrange=camrange*bestscale;}
	
	var myscale = me.model.getScale();

	myscale.setX(bestscale);
	myscale.setY(bestscale);
	myscale.setZ(bestscale);
	
	// HACK: Set the scale to get bigger for the models as altitude increases, as the clip plane moves further from the camera as alt goes up (ANNOYING!!)

	//me.model.setScale(myscale);

	//flags[me.glidernumber].getStyleSelector().getIconStyle().setScale(labelscale*0.75);


// add the trail point to the trailing line, and remove any old ones
// ... also check we aren't over our max trail length, and remove if necessary

//	maxtraillength=0;

  pilotTrail[me.glidernumber].getCoordinates().pushLatLngAlt(me.prevlat,me.prevlon,alt);
  while (pilotTrail[me.glidernumber].getCoordinates().getLength() > maxtraillength) {
	  var removeitem = pilotTrail[me.glidernumber].getCoordinates().shift();
  }
	

// reset the time so we can check cumulative time in our <updateinterval> second window next time

    me.lasttime=me.curtime;
    tickCamera(me.glidernumber);
	
}
