/** * Waypoint Encounter Class for Rob's Moving Map package * @author Robert J Morton * @version 06 January 1998 */ /* Contains data and methods to transact a navigational encounter between an aircraft and a waypoint. Each encounter is a trans- action 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 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 per second (min speed on landing) private boolean approach, // true if aircraft has not yet passed over waypoint toFlag; // to/from flag for the waypoint encounter private waypnt wp, // reference to current waypoint pw, // reference to previous waypoint nw; // reference to next waypoint private static wpenc we; // reference to current waypoint encounter object private aircraft ac; // reference to current aircraft object //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(); // get inbound radial from previous waypoint OutRad = wp.getOutRad(); // get outbound radial to next waypnt (radians) tcr = ac.getTCR(); // get aircraft's turning circle radius tcd = tcr + tcr; // turning circle diameter ThDst = tcd + tcd; // threshold approach distance to decelerate to SPEED TCR = tcr * tcr; // square of turning radius wp.DandB(); // compute range and bearings to this waypoint Dst = wp.getDst(); // find initial distance and bearings for new waypoint RunDst = wp.getDST() / 2; // dist to go past waypnt before capturing next 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 AIRCRAFT AND UPDATES ALL ENCOUNTER VARIABLES boolean enRoute() { if(approach || // if still approaching, or Dst < RunDst) { // not yet gone RunDst past this waypoint // advance the aircraft along its track ac.advance(getReqHdg(),getReqSpd()); pDst = Dst; /* make last time's distance this time's previous distance */ wp.DandB(); // compute distance & bearings Dst = wp.getDst(); // between aircraft and waypoint return true; // return 'aircraft is still encountering waypoint' } return false; // return 'aircraft 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 aircraft approaching waypoint, toFlag = true; // set the 'to' flag true else // else set the 'to' flag false toFlag = false; // (= 'from') if(approach // If the approach flag is still set, && !toFlag // and aircraft is now receding && Dst < tcr) // but is still closer than its turning radius, approach = false; // it is deemed to have passed the waypoint. /* If aircraft close enough to the waypoint for Euclidean approach and turn/retreat geometry, then get short-range radio heading; else, [it must be more than 150 km from either waypoint], so get the trans-oceanic GPS or inertial heading. */ if(Dst < wp.getRange()) ReqHdg = getRadioHeading(); else ReqHdg = getGPSheading(); return ReqHdg = ac.RatAng(ReqHdg); // return the rationalised required } // heading (i.e. in the range 0-2π) // DETERMINE THE REQUIRED SPEED OF THE AIRCRAFT private double getReqSpd() { double x = Dst, // aircraft's distance from waypoint rs; // aircraft's required speed if(wp.getDest() // if the aircraft has passed && !approach) // over the destination waypoint x = -1.75 * x; // gradually reduce speed if((rs = ac.getSPEED() * (1 + x / ThDst)) < RS) rs = RS; // impose a minimum speed return rs; // return required speed (to ac.advance()) } // COMPUTE STEERING OFF-SET FROM WAYPOINT TO GUIDE-CIRCLE TANGENT double getStrOff(double a) { a = ac.BipAng(a - OutRad + π); // deviation from the inbound radial boolean // indicates whether radial right = true; // deviation is right or left if(a < 0) { // If it is to the left of the inbound radial from a = -a; // 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 the distance to where the tangent touches the turning circle. 's' is the steering off-set angle between the bearing to waypoint and the 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 from waypoint bearing required to keep the air- craft on a tangent to the turning circle. Set the steering offset negative if the aircraft is to the 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 AIRCRAFT WITHIN WIND DRIFT CORRIDOR private double getWndOff(double a) { /* Compute the distance of the aircraft from the centre-line of the approach corridor. That is: the aircraft's approach distance from waypoint times the aircraft's deviation from the 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 currently outside the approach confinement corridor, 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's non-zero (it's from another class) if((q /= e) > 10) // Impose an excursion q = 10; // limit on tangent of the else if(q < -10) // steering off-set q = -10; // (or corridor re-entry) angle. s = Math.atan(q); // compute the said angle } } return s; // return it as a steering offset from rBrg } /* The following method computes the required heading from bearing information 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 WAYPOINT-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 still within the corrodor approach run s = getWndOff(tBrg); // keep aircraft within corridor boundaries if(s == 0) // no steering command from corridor confine- s = getStrOff(tBrg); // ment so invoke normal approach steering /* Do not disturb last time's 'ReqHdg' value if 's' is zero, otherwise compute the bearing of the waypoint from aircraft + the steering correction. */ if(s != 0) ReqHdg = rBrg + s; } 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 if(e != 0) { // better check: it comes from another class! /* Impose an excusion limit on the tangent of the steering off-set angle. */ if((d /= e) > 10) d = 10; else if(d < -10) 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 required heading } /* The following method returns a Required Heading value which is relative to the aircraft's own perception of where North is. However, its ability 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. */ // USES ALL BEARINGS RELATIVE TO AIRCRAFT private double getGPSheading() { 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 nw.DandB(); // compute bearing of next waypoint /* Steer twice the reverse of the difference between the bearing of the next waypoint and the extended. */ ReqHdg = 2 * nw.getrBrg() - rBrg - π; } return ReqHdg; // bearing line from the previous waypoint. } boolean getToFlag() {return toFlag;} // return the approach status static wpenc getCurrent() {return we;} // return reference to current } // waypoint encounter object