/** * 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 radian private double sMAX = 1000, // aircraft maximum speed in kilometres per hour LS = 300, // landing speed 300 kph 162 knots SPEED = 400, // 400 kph 217 knots (min speed aircraft slows // to pass over a waypoint) WS = 100, // wind speed 100 kph 54 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 the wind speed WS to earth-radians per second and the direction WD [FROM which the wind is blowing] to radians clockwise from North TO which the wind is blowing (not FROM which it is blowing). */ WS /= KphRps; WD = RatAng(WD / DPR + π); /* Convert waypoint passover SPEED to earth-radians per second and convert radius 'r' of the aircraft's ideal turning circle to earth- radians then determine the distance CM from waypoint at which cor- ridor approach rules terminate. */ SPEED /= KphRps; r /= KPR; CM = 2.1 * r; /* Convert aircraft's default initial heading 'H' to radians, its landing speed in radians per second and its maximum speed sMAX to mean Earth-radians per second. */ H /= DPR; LS /= KphRps; sMAX /= KphRps; } void advance(double rh, double rs) { // ADVANCE AND ROTATE THE AIRCRAFT /* COMPUTE ELAPSED TIME (IN MILLISECONDS) SINCE LAST PASS OF THE PROGRAM Get the current system time in milliseconds. If this is the if first time through, set current time and exit because you have no previous reference time. Otherwise, find the elapsed time since last pass in milliseconds and save the current time for the next pass. */ long t = System.currentTimeMillis(); if(FTT) { T = t; FTT = false; return; } double δt = (double)(t - T) / 1000; T = t; /* 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 is computed a damped rate-of-tunr increment for this pass of the pro- gram. 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 requ- ired rate of turn that was computed during the previous pass of the pro- gram. This increment is then added to aircraft's previous rate of turn. INCREMENT THE AIRCRAFT'S RATE-OF-TURN BY the bipolarized version of max- imum 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 all multiplied by elapsed time since last pass of 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. Thus, new thrust heading = old thrust heading minus 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's 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 max- imum permitted rate of turn. The imposed limit on air speed is computed as SPEED (Earth-radians per second) multiplied by the maximum permitted rota- tional velocity W (radians per second) divided by the aircraft's current rate of turn v (radians/second). If the delivered required speed is great- er than this limit then the required speed is set to this limit. Get magnitude of the current rate of turn. If the aircraft is turning at more than 10 degrees per minute, set speed limit decreed by rate of turn and restrict required air speed to that allowed by the rate of turn. */ double v = Math.abs(w); if(v > V) { v = SPEED * W / v; if(v < rs) rs = v; } /* ADVANCE THE AIRCRAFT ALONG ITS GROUND TRACK: Computes the distance the aircraft advances due to engine thrust and distance it advances due to wind. The distance 'd' that the aircraft has travelled since last the pass through this program is the aircraft's speed in earth-radians/second times number of seconds that have elapsed since last pass. */ double d = (s += (rs - s) * δt / 6) * δt; /* distance blown by the wind since the last pass of the program. The wind is ineffective if aircraft is on the ground. */ double wd = WS * δt; if(s < LS) wd = 0; /* COMPUTE THE AIRCRAFT'S NEW POSITION Compute the latitudinal and longitudinal increments in the aircraft's pos- ition since the last pass of this program. Then add these increments to the aircraft's actual latitude and longitude respectively. The 'if' condition avoids division by zero. Advance the aircraft's west- to-east component due to engine thrust. Advance its west-to-east compon- ent due to wind. Convert great circle radians to local longitudinal rad- ians and advance the aircraft longitudinally. δLng is expressed in lati- tude-compensated Earth-radians. */ if((k = Math.cos(Lat)) != 0) { δLng = (d * Math.sin(h) + wd * Math.sin(WD)) / k; Lng += δLng; } /* Advance the aircraft's south->north component due to engine thrust. Advance its south->north component due to wind. δLat is necessarily expressed in normal Earth-radians. */ δLat = (d * Math.cos(h) + wd * Math.cos(WD)); Lat += δLat; /* WIND COMPEMSATOR Computes the amount by which the required heading (true bearing to the next waypoint) must be biased in order for part of the aircraft's engine thrust to cancel the cross-component of the current wind and thereby cause the aircraft to truly follow the required heading. */ if(δLat != 0) { /* avoid division by zero if aircraft passes over North or South pole */ /* 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. */ ψ = 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 change in longitude is negative if(δLat < 0) // then if change in latitude is negative ψ += π; // complement the latitude to make it negative else // else change in longitude is positive ψ = Twoπ - ψ; // latitude is positive else if(δLat < 0) // Else, δLng is positive, so if δLat negative ψ = π - ψ; // latitude is negative δψ = RatAng(h - ψ); // track heading error due to wind } } // Reset the aircraft to the starting point of its route. void reset(waypnt w) { 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 getROT() {return -w;} // return rate of turn radians/second double getSPEED() {return SPEED;} // return standard speed factor void setHdg(double x) {h = x;} // set aircraft heading // Return the minimum distance for corridor approach rules. double getCM() {return CM;} // Return the minimum comfortable turning circle in kilometres. double getTCR() {return r;} // Return the aircraft's current speed. double getSpd() { if(s > sMAX) s = sMAX; return s; } /* Return the base distance 'APD' from the aircraft to the aiming point for steering the aircraft back on track whenever it drifts off course. This is given by the radius 'r' of the aircraft's turning circle 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 an 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; } }