/** * Waypoint Encounter Class for Rob's Moving Map package * @author Robert J Morton YE572246C * @version 06 January 1998 */ /* Contains data and methods to transact a navigational encounter between an aircraft and a waypoint. Each encounter is a transaction with a transient lifespan which lasts from when the aircraft enters the waypoint's range of influence until it leaves it. */ // Waypoint Encounter (Spherical Geometry version) class wpenc implements navconst { private static wpenc we; // reference to current waypoint encounter object private aircraft ac; // reference to current aircraft object private waypnt wp; // reference to current waypoint private waypnt pw; // reference to previous waypoint private waypnt nw; // reference to next waypoint private boolean approach, // true if aircraft has not yet passed over waypoint toFlag; // to/from flag for the waypoint encounter private double InRad, // inbound radial from previous waypoint OutRad, // outbound radial to next waypoint rBrg, // bearing of waypoint from aircraft tBrg, // bearing of aircraft from waypoint ReqHdg, // heading the aircraft must fly pDst, // previously computed distance to waypoint Dst, // currently computed distance to waypoint tcr, // radius of aircraft's minimum desired turning circle tcd, // diameter of turning circle - double the above TCR, // square of aircraft's turning circle radius RunDst, // distance to run past this waypoint before acquiring next ThDst, // threshold approach distance to decelerate to SPEED DL, // half-width of approach corridor CM, // distance at which corridor rules end on approach RS = 50 / KphRps; // 50 kph in radians/second (min speed on landing) // CONSTRUCTOR: SET UP FOR APPROACH WITH NEW OUTBOUND RADIAL wpenc(waypnt w, aircraft ac) { we = this; // reference current waypoint encounter object this.ac = ac; // reference to aircraft object wp = w; // get the reference of the current waypoint waypnt.setCurrent(wp); // set the reference of the current waypoint pw = wp.getPrev(); // reference to previous waypoint object nw = wp.getNext(); // reference to next waypoint object InRad = wp.getInRad(); // inbound radial from previous waypoint OutRad = wp.getOutRad(); // outbound radial to next waypoint [radians] tcr = ac.getTCR(); // Get aircraft's turning circle radius, the square TCR = tcr * tcr; // of it, its diameter and twice its diameter tcd = tcr + tcr; // (which is the aircraft's threshold approach ThDst = tcd + tcd; // distance to decelerate to its landing SPEED. wp.DandB(); // Compute range and bearings to this waypoint and Dst = wp.getDst(); // find initial distance & bearings of new waypoint. RunDst = wp.getDST() / 2; // Distance to run past this waypoint // before capturing the next one. CM = ac.getCM(); // corridor rules cease when aircraft gets this close pDst = Dst + .1; // initialise previous distance value approach = true; // A waypoint encounter always starts off with toFlag = true; // the aircraft leaving the starting point DL = wp.getDL(); // half-width of approach corridor for current waypoint } // ADVANCES THE AIRCRAFT AND UPDATES ALL THE ENCOUNTER VARIABLES boolean enRoute() { /* If the aircraft is still approaching the waypoint or not yet gone a distance of RunDst past the waypoint, then advance the aircraft along its track, make last time's distance this time's previous dist- ance, compute distance & bearings between aircraft and waypoint and signal TRUE that the aircraft is still encountering the waypoint. */ if(approach || Dst < RunDst) { ac.advance(getReqHdg(),getReqSpd()); pDst = Dst; wp.DandB(); Dst = wp.getDst(); return true; } return false; // Otherwise, return FALSE, indicating that the } // air craft has just finished this encounter. // COMPUTE THE HEADING ON WHICH THE AIRCRAFT MUST FLY private double getReqHdg() { tBrg = wp.gettBrg(); // bearing of the aircraft viewed from the waypoint rBrg = wp.getrBrg(); // bearing of the waypoint viewed from the aircraft if(Dst < pDst) // If the aircraft is approaching waypoint, toFlag = true; // set the 'to' flag true else // else toFlag = false; // set the 'to' flag false (= 'from'). if(approach && !toFlag // If aircraft is now receding from the waypoint && Dst < tcr) // but is still closer than its turning radius, approach = false; // it is deemed to have passed the waypoint. /* If the aircraft is close enough to the waypoint to be able to use Euclidean approach & turn/retreat geometry, then get short-range radio heading. */ if(Dst < wp.getRange()) ReqHdg = getRadioHeading(); /* Otherwise, if more than 150 kilometres from either waypoint, get the trans-oceanic GPS or inertial heading. */ else ReqHdg = getGPSheading(); // return the rationalised (0-2π range) required heading return ReqHdg = ac.RatAng(ReqHdg); } // DETERMINE THE REQUIRED SPEED OF THE AIRCRAFT private double getReqSpd() { // aircraft's current distance from the waypoint, its required speed if(wp.getDest() // If aircraft has passed over && !approach) // the destination waypoint, x = -1.75 * x; // decelerate it by the appropriate amount. /* If this results in the aircraft falling below its minimum speed for passing a waypoint, set its speed to the minimum allowed. */ if((rs = ac.getSPEED() * (1 + x / ThDst)) < RS) rs = RS; return rs; // return required speed (to ac.advance()) } // COMPUTE STEERING OFF-SET FROM THE WAYPOINT TO THE GUIDE-CIRCLE TANGENT double getStrOff(double a) { a = ac.BipAng(a - OutRad + π); // deviation from the inbound radial // Indicates whether radial deviation is right or left boolean right = true; if(a < 0) { // If it is to the left of the inbound radial a = -a; // from the waypoint's point of view, make it positive right = false; // and use the flag to indicate its sense. } /* Now is a good time to refer to the Waypoint Encounter 'Pre-Capture' diagram in AirNav6.htm. 'e' is the square of distance to where tangent touches circle. 's' is the steer- ing off-set: angle between bearing to waypoint and tangent. */ double b = tcr * Math.cos(a), c = Dst - tcr * Math.sin(a), e = b * b + c * c - TCR, s = 0; /* If the aircraft has not yet reached the turning circle, compute the steering offset 's' from the waypoint bearing required to keep the aircraft on a tangent to the turning circle. Set the steering offset negative if aircraft to right of OutRad + π */ if(e > 0 && c != 0) { s = Math.atan2(b, c) - Math.atan2(tcr,Math.sqrt(e)); if(right) s = -s; } return s; // return the required steering offset } // KEEP THE AIRCRAFT WITHIN THE WIND DRIFT CORRIDOR private double getWndOff(double a) { /* The aircraft's approach distance from waypoint TIMES the aircraft's deviation from inbound radial InRad PLUS the half-width of the approach confinement corridor */ double q = Dst * Math.sin(ac.BipAng(a - InRad)) + DL, s = 0; // default value of corridor re-entry steering off-set /* If the aircraft is outside the approach confinement corridor then get the 'along-track' side of the steering triangle. */ if(Math.abs(q) > Math.abs(DL)) { double e = ac.getAPD(); if(e != 0) { // Provided it is non-zero if((q /= e) > 10) // (because it's from another class!), q = 10; // impose an excursion limit else if(q < -10) // on tangent of the steering off-set q = -10; // (or corridor re-entry) angle s = Math.atan(q); // and compute the said angle. } } return s; // return it as a steering offset from rBrg } /* The following method computes the required heading from bearing informa- tion which is relative to the waypoint's North, not the aircraft's north. However, the aircraft can only set its heading relative to its own view of which direction North is in. The returned ReqHdg will therefore be accurate only up to about 150km. This is of course different if the waypoint itself is supplying steering information as in the case of an ILS. */ // USING WAY-POINT CENTRED BEARING INFORMATION private double getRadioHeading() { if(approach) { // If aircraft currently in approach phase... double s = 0; // set default for steering off-set angle if(Dst > CM) // if aircraft still on the corrodor approach s = getWndOff(tBrg); // run, keep it within the corridor boundaries. /* If steering command from corridor confinement computation is zero, i.e. there is no steering command, invoke normal approach steering. */ if(s == 0) s = getStrOff(tBrg); /* If 's' is still zero, do not disturb last time's ReqHdg value but if it is now non-zero, compute the bearing of way- point from aircraft + its steering correction. */ if(s != 0) ReqHdg = rBrg + s; } else { // else, if receding, track the waypoint's outbound radial double d = Dst * Math.sin(ac.BipAng(OutRad - tBrg)), e = ac.getAPD(); // base of steering triangle // Check that 'e' is non-zero because it comes from another class. if(e != 0) { // Provided 'e' is non-zero, if((d /= e) > 10) // impose an exclusion limit d = 10; // on the tangent 'd' else if(d < -10) // of the steering off-set angle. d = -10; /* Off-set the required heading by the amount required to bring the aircraft back on track along the outbound radial to the next waypoint. */ ReqHdg = OutRad + Math.atan(d); } } return ReqHdg; // return the required heading } /* The following method returns a Required Heading value which is relative to correct for wind drift gets worse the greater the distance between way points. There is a better (but more complicated) treatment which makes the corrective angle proportional to the actual distance off course. */ private double getGPSheading() { // USES ALL BEARINGS RELATIVE TO AIRCRAFT if(toFlag) { // If approaching current waypoint pw.DandB(); // compute bearing of previous waypoint ReqHdg = 2 * rBrg - pw.getrBrg() - π; } else { // Else [if receding from current waypoint] /* Compute the bearing of the next waypoint and steer twice the reverse of the difference between the bearing of the next waypoint and the ext- ended bearing line from the previous waypoint. */ nw.DandB(); ReqHdg = 2 * nw.getrBrg() - rBrg - π; } return ReqHdg; } boolean getToFlag() {return toFlag;} // return the approach status static wpenc getCurrent() {return we;} // return reference to current } // waypoint encounter object.