/** * Aircraft Class: part of the Moving Map package * @author Robert J Morton * @version 01 January 1998 */ /* Provides the navigational essentials of an aircraft - starting position, current position, speed. Also the radius of minimum ideal (comfortable) turning radius for the type of aircraft concerned. The square of this radius is also stored to save on computation time in other classes. */ class aircraft implements navconst { private static aircraft ac; // reference to current aircraft object private boolean FTT = true, // first time through flag for position update method STT = true; // second time through flag for position update method private static final double W = 0.1, // maximum permitted rotational velocity radians/second ROT = 0.03, // maximum rate of turn in radians per second V = TenDeg / 60, // 10 degrees per minute in radians per second DH = 0.5; // smoothing kicks in when heading error < half a radian private double sMAX = 1000, // aircraft maximum speed in kilometres per hour // 400 kph 217 knots (min speed aircraft slows to pass over a waypoint) SPEED = 400, LS = 300, // landing speed 300 kph 162 knots WS = 20, // wind speed 20 kph 10.8 knots WD = 230, // wind from slightly west of south-west in degrees Lat, // aircraft's current latitude Lng, // aircraft's current longitude δLat, // aircraft's latitudinal increment this pass δLng, // aircraft's longitudinal increment this pass H = 230, // aircraft's default initial heading (in degrees) h, // aircaft's current (thrust) heading (in radians) ψ, // true ground-track heading (in radians) δψ = 0, // heading correction for wind (in radians) rh, // aircraft's required heading (in radians) s, // aircraft speed in Earth-radians per second w = 0, // aircraft's current rate of turn (radians per second) r = 10, // radius of aircraft's ideal turning circle (km) CM; // distance from waypoint at which corridor approach rules terminate private long T; // system time at which previous positional update occurred aircraft() { // CONSTRUCTOR FOR THE AIRCRAFT OBJECT ac = this; // reference to current aircraft object /* Convert direction of wind to radians clockwise from North to which the wind is blowing (not from which it is blowing) */ WD = RatAng(WD / DPR + π); WS /= KphRps; // convert wind speed to earth-radians per second SPEED /= KphRps; /* convert waypoint passover speed to earth-radians per second */ r /= KPR; /* convert radius of aircraft's ideal turning circle to earth-radians */ CM = 2.1 * r; /* distance from waypoint at which corridor approach rules terminate */ H /= DPR; // convert aircraft's default initial heading to radians LS /= KphRps; // landing speed in radians per second sMAX /= KphRps; /* aircraft's maximum speed in mean Earth-radians per second */ } // ADVANCE AND ROTATE THE AIRCRAFT void advance(double rh, double rs){ // COMPUTE ELAPSED TIME (IN MILLISECONDS) SINCE LAST PASS OF THE PROGRAM long t = System.currentTimeMillis(); // current system time if(FTT) { // If first time through, T = t; // set current time, FTT = false; // set that first-time-through has been done return; // and exit. } // elapsed time since last pass in milliseconds double δt = (double)(t - T) / 1000; T = t; // save current time for the next pass /* COMPUTE NEW RATE OF TURN DAMPING FACTOR FOR THE AIRCRAFT Firstly the aircraft's current heading error is computed. This is the difference between direction in which the aircraft is currently heading and the direction in which it should be heading, including compensation for wind drift. This heading error is then used as the basis for computing the damping factor to be applied to the aircraft's maximum permitted rate of turn for correcting this heading error. */ this.rh = rh; // capture the required heading (command heading) /* The rate of turn damping factor is last time's aircraft (thust) heading minus this time's required heading minus this time's ground-track heading error due to wind divided by the upper heading error limit for damping. */ double k = BipAng(h - rh - δψ) / DH; /* COMPUTE AIRCRAFT'S NEW THRUST HEADING First compute a damped rate-of-tune increment for this pass of the program. This is the maximum permitted rate of turn multiplied by the damping factor computed above (ie a new required rate of turn) minus the old required rate of turn that was computed during the previous pass of the program. This increment is then added to the aircraft's previous rate of turn. */ /* INCREMENT THE AIRCRAFT'S RATE-OF-TURN by the bipolarized version of the maximum permitted rate of turn multiplied by the rate of turn damping factor (limited to ± 1) minus last time's actual rate of turn, all limited to ± W then all multi- plied by the elapsed time since the last pass of the program. */ w += limitTo( BipAng( ROT * limitTo(k,1) - w ), W ) * δt; /* The aircraft's new thrust heading is its old thrust heading + the new rate-of-turn increment times the time that has elapsed since the previous pass of the program. */ // New thrust heading = old thrust heading - new permitted heading h = RatAng(h - w * δt); /* CAP THE AIRCRAFT'S AIR SPEED IF NECESSARY: firstly, an upper permitted speed limit is computed that is determined by the aircraft's new rate-of- turn. SPEED is the speed limit for passing a waypoint. It is the maximum permitted air speed for that aircraft's maximum permitted rate of turn. The imposed limit on air speed is computed as SPEED (Earth-radians per second) multiplied by he maximum permitted rotational velocity W (radians per second) divided by the aircraft's current rate of turn v (radians per second). If the delivered required speed is greater than this limit then the required speed is set to this limit. */ double v = Math.abs(w); // magnitude of aircraft's current rate of turn if(v > V) { // if rate of turn > 10 degrees/minute v = SPEED * W / v; // set speed limit decreed by rate of turn if(v < rs) // restrict required air speed rs = v; // to that allowed by rate of turn } /* ADVANCE AIRCRAFT ALONG ITS GROUND TRACK. Computes distance the aircraft advances due to engine thrust and the distance it advances due to wind. */ /* Distance travelled since last time through is aircraft speed in earth-radians per second times number of seconds elapsed since last time through. */ double d = (s += (rs - s) * δt / 6) * δt; double wd = WS * δt; // distance blown by wind since last time through. if(s < LS) wd = 0; // wind is ineffective if aircraft is on the ground /* COMPUTE THE AIRCRAFT'S NEW POSITION First compute the latitudinal and longitudinal increments in the aircraft's position since the last pass of this program. It then adds these increments to the aircraft's actual latitude and longitude respectively. */ if((k = Math.cos(Lat)) != 0) { // avoid division by zero /* advance west->east component due to engine thrust, advance its west->east component due to wind convert great circle radians to local longitudinal radians */ δLng = (d*Math.sin(h) + wd*Math.sin(WD)) / k; Lng += δLng; // advance the aircraft longitudinally. NB: δLng is } // expressed in latitude-compensated Earth-radians /* advance its south->north component due to engine thrust advance its south->north component due to wind */ δLat = (d * Math.cos(h) + wd * Math.cos(WD)); Lat += δLat; // δLat is necessarily expressed in normal Earth-radians /* WIND COMPEMSATOR: computes the amount by which the required heading (true bearing to the next waypoint) must be biased so that part of the aircraft's engine thrust to cancel the cross-component of current wind and thereby cause the aircraft to truly follow the required heading.*/ // avoid division by zero if aircraft passes over North or South pole if(δLat != 0) { /* Compute true ground track heading as an angle of the positive quadrant. Math.atan() does not appear to yield expected results outside the positive-only quadrant. Compute the aircraft's true ground track heading */ ψ = Math.atan(Math.abs(δLng * k) / Math.abs(δLat)); /* Then, according to the signs of δLng and δLat, work out the true ground-track as a full-circle compass bearing. */ if(δLng < 0) // if longitude is negative, if(δLat < 0) // if latitude negative ψ += π; // add pi to true heading make it a 0 to 360 angle else // else [latitude is positive] ψ = Twoπ - ψ; // so subtract true heading from a full circle else // else [longitude is positive] if(δLat < 0) // so, if latitude is negative ψ = π - ψ; // subtract true heading from half a circle δψ = RatAng(h - ψ); // track heading error due to wind } } void reset(waypnt w) { // reset aircraft to its stating position Lat = w.getLat(); // set aircraft's initial latitude Lng = w.getLng(); // set aircraft's initial longitude h = H; // set aircraft to its initial thrust heading ψ = H; // set aircraft to its initial true heading δψ = 0; // set wind drif error to zero FTT = true; // reset the first time through flag STT = true; // reset the second time through flag } void start() {FTT = true;} // re-start the aircraft void stop() {s = 0;} // stop the aircraft at end of flight double getLat() {return Lat;} // return aircraft latitude double getLng() {return Lng;} // return aircraft longitude double getHdg() {return h;} // return aircraft heading double getRqH() {return rh;} // required heading double getSpd() { // return aircraft's current speed if(s > sMAX) s = sMAX; return s; } // return rate of turn (rate of turn) radians/second double getROT() {return -w;} // return minimum comfortable turning circle in kilometres double getTCR() {return r;} // return minimum distance for corridor approach rules double getCM() {return CM;} double getSPEED() {return SPEED;} // return standard speed factor void setHdg(double x) {h = x;} // set aircraft heading /* RETURN BASE DISTANCE TO AIMING POINT FOR STEERING AIRCRAFT BACK ON TRACK WHEN IT DRIFTS OFF. */ /* aircraft's turning circle radius times the number of thousands of kph it is doing + 1 */ double getAPD() {return r * (1 + s / TkphRPS);} double BipAng(double a) { // MAKE AN ANGLE LIE BETWEEN -π AND +π while(a > π) a -= Twoπ; while(a <= -π) a += Twoπ; return a; } double RatAng(double a) { // MAKE ANGLE LIE BETWEEN 0 AND 2π while(a >= Twoπ) a -= Twoπ; while(a < 0) a += Twoπ; return a; } private double limitTo(double x, double y) { if(x > y) x = y; else if(x < -y) x = -y; return x; } }