The Aircraft Class

The main function of this class is to advance() the aircraft along the currently selected route. It does this every half second (assuming that the time frame constant TF in MovMap.class has not been altered from 500 milliseconds since this was written).

Speed

The aircraft is advanced along its route by an amount equal to the distance it would travel at its current speed within the length of time specified by TF. This distance is then resolved into its south-north and west-east increments. The aircraft's latitude and longitude are then updated by these increments. The speed which the aircraft is required to attain is provided by the waypoint encounter class WpEnc. This will invariably be somewhat different from its actual current speed. This is because the aircraft is imbued with an unnaturally large amount of linear inertia. This causes the aircraft only ever to manage to accelerate or decelerate part of the way towards the speed at which it is required to travel.

Turn

The aircraft is also at the same time rotated as necessary. At each time frame, the waypoint encounter class WpEnc provides the heading rh which the aircraft is required to take in order to follow the route. However, the aircraft is also imbued with an unnatural amount of rotational inertia. The heading it achieves is therefore somewhere in between its current heading h and the required heading rh . The aircraft's acquisition of the true required heading can be further delayed by the fact that an upper limit is imposed upon the aircraft's rate of turn.

Inertia

The reason for the unnaturally large amounts of both linear and rotational inertia is to exercise - and thereby demonstrate - the guidance method's ability to bring the aircraft back on course when perturbed.

Wind

Normally, the navigation system would be advised of current wind speed and direction so it could subtract its components from the aircraft's air speed. However, to compound the exercising of the guidance methods, a 50kph south-west wind error (actually from bearing 230) is simulated. That is, a wind drift element of which the navigation software is unaware.

The wind error's speed is also resolved into northings and eastings which are added to those of the aircraft's speed when its latitude and longitude are updated. Bear in mind the wind when observing the irregularities with which the aircraft makes its turns at some of the waypoints. Wind and weather change an aircraft's encounter with a waypoint from a conventional 'two-body' problem into a complex dynamical one with its inevitable element of chaos. Its ability to handle this kind of situation is the real proof of a guidance system.

This class's other function is to reset the aircraft's position to the first, next or previous waypoint along the currently selected route.

/**
  * 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 double V = TenDeg / 60;   //10 degrees per minute in radians per second
   private boolean FTT = true;       //first time through flag for position update method
   private double Lat;               //aircraft's current latitude
   private double Lng;               //aircraft's current longitude
   private double h;                 //aircaft's current heading (in radians)
   private double rh;                //aircraft's required heading (in radians)
   private double H = 230;           //aircraft's default initial heading
   private double s;                 //aircraft speed in kilometres per second
   private double SPEED = 750;       //750 kph (min speed aircraft slows to to pass over a waypoint)
   private double w = 0;             //aircraft's current rate of turn (radians per second)
   private double W = .1;            //maximum permitted rotational acceleration radians/second/second
   private double ROT = .03;         //maximum rate of turn in radians per second (1.5 radians per minute)
   private double DH = .5;           //smoothing kicks in when heading error < half a radian
   private double r = 10;            //radius of aircraft's ideal turning circle (km)
   private double CM;                //distance from waypoint at which corridor approach rules terminate
   private double WS = 50;           //wind speed 50 kph
   private double WD = 230;          //wind from slightly west of south-west in degrees
   private long T;                   //system time at which previous positional update occurred
   private double LS = 300;          //landing speed 300 kph

   aircraft() {                      //CONSTRUCTOR FOR THE AIRCRAFT OBJECT
      ac = this;                     //reference to current aircraft object
      WD = RatAng(WD / DPR + Pi);    //convert wind direction to radians clockwise from North
      WS /= KphRps;                  //convert wind speed to radians per second
      SPEED /= KphRps;               //convert waypoint passover speed to radians per second
      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;                 //convert landing speed to radians per second
   }

   void advance(double x, double rs) {        //ADVANCE AND ROTATE THE AIRCRAFT
      rh = x;                                 //capture required heading
      long t = System.currentTimeMillis();    //current system time
      if(FTT) {                               //if first time through
         T = t; FTT = false; return;          //set current time and exit
      }
      double dt = (double)(t - T) / 1000;     //elapsed time since last time through in milliseconds
      T = t;                                  //save current time for next time through
      double k = BipAng(                      //The rate of turn damping factor is
                 h                            // last time's actual heading
                   - rh)                      // minus this time's required heading
                 / DH;                        // divided by the upper heading error limit for damping.
      if (k > +1) k = +1;                     //impose maximum limit for rate of turn
      else if (k < -1) k = -1;                //impose minimum limit for rate of turn
      double dw = BipAng(                     //Desired rotational acceleration is
                  k * ROT                     // the new desired rotational velocity
                  - w   );                    // minus last time's actual rotational velocity.
      if(dw > +W) dw = +W;                    //impose maximum limit on rotational acceleration
      else if(dw < -W) dw = -W;               //impose minimum limit on rotational acceleration
      w += dw * dt;                           //increase rotational velocity by rotational acceleration * elapsed time
      h = RatAng(h - w * dt);                 //New actual heading is old actual heading less the new permitted heading
      double v = Math.abs(w);                 //magnitude of the current rate of turn
      if(v > V) {                             //if the aircraft is turning
         v = SPEED * W / v;                   //set speed limit decreed by rate of turn
         if(v < rs) rs = v;                   //restrict required speed to that allowed by rate of turn
      }
      double d                                //Distance travelled since last time through is
               = (s += (rs - s) * dt / 8)     // aircraft speed in kilometres per second
               * dt;                          // times number of seconds elapsed since last time through.
      double wd = WS * dt;                    //distance blown by wind since last time through.
      if(s < LS) wd = 0;                      //wind is ineffective if aircraft is on the ground
      k = Math.cos(Lat);                      //to convert great circle radians to local longitudinal radians
      if(k != 0)                              //avoid division by zero
         Lng += (  d * Math.sin(h)            //advance its west->east component due to engine thrust
                + wd * Math.sin(WD) )         //advance its west->east component due to wind
                 / k;        
      Lat += (  d * Math.cos(h)               //advance its south->north component due to engine thrust
             + wd * Math.cos(WD) );           //advance its south->north component 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 heading
      FTT = true;                      //reset the first 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 s;}         //return aircraft speed
   double getROT() {return -w;}        //return rate of turn (rotational velocity) radians/second
   double getTCR() {return r;}         //return minimum comfortable turning circle in kilometres
   double getSPEED() {return SPEED;}   //return standard speed factor
   double getCM() {return CM;}         //return min dist for corridor approach rules
   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.
   double getAPD() {
      return r * (1 + s / TkphRPS);    //aircraft's turning circle radius
   }                                   //times the number of thousands of kph it is doing + 1

   double BipAng(double a) {           //MAKE AN ANGLE LIE BETWEEN -PI AND +PI
      while(a > Pi) a -= TwoPi;
      while(a <= -Pi) a += TwoPi;
      return a;
   }

   double RatAng(double a) {           //MAKE ANGLE LIE BETWEEN 0 AND 2PI
      while(a >= TwoPi) a -= TwoPi;
      while(a < 0) a += TwoPi;
      return a;
   }

   static aircraft getCurrent() {return ac;}  //return reference to current aircraft object
}

/*  Robert J Morton, the author of this program, 
    is a poor but Right Honourable Member of the
    Ancient and Noble Order of the Long-term Unemployed.

    Offers of work please to: robmorton@clara.net  */