WaBis

walter.bislins.ch

Datei: brakesim.js

Inhalt der Datei: media/brakesim.js
// Simulation des Bremsvorganges eines Verkehrsflugzeugs
// http://walter.bislins.ch/
//
// Dependencies:
// - engine.js

function CPlane() {
  // Airbus A320-200

  // simulation params
  this.BrakingDeltaTime = 0.02;       // [s]
  this.OszillationDeltaTime = 0.1;   // [s]
  this.StopSimulationOnHalt = false;

  // environment
  this.Altitude = 500.0;         // [m] (over mean see level)
  this.QNH = 101325;             // [Pa] = [N/m^2] = [kg/m/s^2]
  this.Temperature = 15;         // [C]
  this.WindSpeed = 0;            // [m/s] (+ Headwind/ - Tailwind)
  this.MPD = 1.75;               // [mm] Meand Profile Depth of Runway
  this.F60 = 0.4;                // friction at 60 km/h
  this.CR0 = 3.7699e-3;          // [N^-3] Roll coefficient const
  this.CR1 = 4.60824e-5;         // [m^-1 N^-3] Roll coefficient const
  this.Slope = 0.0;              // [rad]

  // start conditions
  this.LandingWeight = 61200.0;  // [kg]
  this.CG = 25;                  // [% MAC]
  this.AoA = -0.034906;          // [rad] (-2 deg)
  this.Vapp = 70.47;             // [m/s] (132 + 5 = 137 kt)
  this.VFlareReduction = 5.144;  // [m/s] (1-7% + 2-4% Vapp)
  this.SpoilerExtendDelay = 0.5;
  this.SpoilerTransTime = 3.0;   // [s]
  this.SpoilerExtend = 1.0;      // 0..1
  this.TrimTransTime = 5.0;      // [s], Trim Neutral is triggered by SpoilerExtend
  this.FlapsExtend = 1.0;        // 0..1
  this.Reverse = 1.0;            // N1 rate (0..1) = (idle..maxrev), < 0 = no reverse
  this.ReverseDelay = 3.5;
  this.ReverseSpeedLimit = 30.864;  // [m/s] (60kt)
  this.ReverseIdleSpeed = 15.432;   // [m/s] (30kt)
  this.ReverseTransTime = 1.5;   // [s]
  this.SpoolTime = 3.0;          // [s]
  this.Thrust = 0.0;             // N1 rate (0..1) = (idle..1), < 0 = engines off
  this.MaxBrakeForce = 360000.0; // N (entspricht ca. 6m/s^2 Verzögerung = max Autobrake)
  this.ManualBraking = 0.0;      // 0..1 as % of MaxBrakeForce
  this.ManBrakeSpeed = 10.288;   // [m/s] apply manual braking below this speed (20 kt)
  this.ManBrakeTransTime = 1.0;  // [s]
  this.AutoBrake = 0.57;         // 0.57 = LOW = 1.7 m/s^2, 1 = MED = 3 m/s^2
  this.AutoBrakeDelay = 0.0;     // [s]: 0 = Auto: (< 0.6) -> 4s; (>= 0.6) -> 2s after SpoilerExtendDelay
  this.AutoBrakeTransTime = 3.0; // [s]
  this.ParkingBrakeSet = false;
  this.NEngines = 2;
  this.engine = new CTurbofan()

  // simulated values
  this.Cr = 0.0;                 // Roll coefficient f(Speed,FMz)
  this.Mue = 0.0;                // Friction f(F60,MPD,speed)
  this.Sp = 0.0;                 // [m/s] Speed Constant
  this.CL = 0.0;                 // Lift coefficient
  this.CD = 0.0;                 // Drag coefficient
  this.CT = 0.0;                 // [N s^2 / m^2]: Trim coefficient
  this.Rho = 0.0;
  this.ForceAcceleration = 0.0;  // [N]
  this.ForceWeight = 0.0;        // [N]
  this.ForceSlope = 0.0;         // [N]
  this.ForceLift = 0.0;          // [N]
  this.ForceTrim = 0.0;          // [N]
  this.ForceDrag = 0.0;          // [N]
  this.ForceMainGear = 0.0;      // [N]
  this.ForceNoseGear = 0.0;      // [N]
  this.ForceGear = 0.0;          // [N]
  this.ForceMainBrake = 0.0;     // [N]
  this.ForceNoseBrake = 0.0;     // [N]
  this.ForceBrake = 0.0;         // [N] Note: ForceBrake includes ForceRoll!!!
  this.ForceRoll = 0.0;          // [N]
  this.ForceEngines = 0.0;       // [N]
  this.Acceleration = 0.0;       // [m/s^2]
  this.Speed = 0.0;              // [m/s] Ground Speed
  this.IsRolling = true;
  this.HasStopped = true;
  this.Distance = 0.0;           // [m]
  this.Time = 0.0;               // [s]
  this.V0Time = 0.0;             // [s] time of last speed < Vmin
  this.DeltaDistance = 0.0;      // [m] used to compute energies (Force times distance)
  this.EnergyBrake = 0.0;        // [Nm=J] total energy applied by brakes
  this.EnergyRoll = 0.0;         // [Nm=J] total energy part of roll resitance
  this.EnergyDrag = 0.0;         // [Nm=J] total energy part of drag
  this.EnergyEngines = 0.0;      // [Nm=J] total energy applied by engintes (reversers)
  this.TotalEnergy = 0.0;        // [Nm=J]

  // aircraft specific constants
  this.Pwheel = 1448000;         // [N/m^2] tyre inflation pressure = 210 psi
  this.CLa = 5.16;               // Lift coefficients
  this.CL0 = 0.2;
  this.CLflaps = 1.12;
  this.CLspoiler = -0.36;        //
  // this.CD = 0.2293;           // [http://brahimtahiri.bravehost.com/03_ContaminatedRunway.pdf]
  this.CD0 = 0.02;               // sa_knebel.pdf P. 24
  this.CD2 = 0.04;
  this.CDflaps = 0.08;           // sa_knebel.pdf P. 24
  this.CDspoiler = 0.1293;       // http://brahimtahiri.bravehost.com/03_ContaminatedRunway.pdf
  this.CM = -0.05;               // Momentenbeiwert (pitching moment coefficient)
  this.WingArea = 122.6;         // [m^2]
  this.WingSpan = 34.10;         // [m] (Wikipedia); not used in any computation
  this.MAC = 4.1935;             // [m] (mean aerodynamic chord)
  this.PCGx = 16.31;             // [m] default, will be computed from CG
  this.PCGz = 2.6;               // [m] estimated Pos of CG from ground
  this.PFx = 5.07;               // [m] Pos of nose gear
  this.PMx = 17.71;              // [m] Pos of main gear
  this.PLx = 16.31;              // [m] Pos of aerodynamic center
  this.PLz = 2.6;                // [m] Pos of aerodynamic center from ground (estimated)
  this.PTx = 33.898;             // [m] Pos of ac of trimm
  this.PJz = 1.7;                // [m] Pos of Engine from ground
  this.AutoBrakeLimit = 3.0;     // [m/s^2] LOW = 1, MED = 2, MAX = 3
  this.XL = 0.0;                 // [m] CenterGravity to CenterLift
  this.ZL = 0.0;                 // [m]
  this.XT = 0.0;                 // [m] CenterGravity to CenterTrimm
  this.ZJ = 0.0;                 // [m] CenterGravity to CenterEngine
  this.ZM = 0.0;                 // [m] CenterGravity to Ground
  this.XM = 0.0;                 // [m] CenterGravity to MainGear
  this.XF = 0.0;                 // [m] CenterGravity to NoseGear
  this.Kf = 200000.0;            // [N/m] Federkonstante
  this.Kd = 200000.0;            // [Ns/m] Dämpfung

  // general constants used
  this.GravityConstant = 9.80665;// [m/s^2]
  this.GasMass = 0.0289644;      // [kg/mol]
  this.GasConstant = 8.31446;    // [J/K/mol]
  this.NormTemp = 288.15;        // [K]
  this.LapseRate = 0.0065;       // [K/m]

  // internal simulation params
  this.DeltaTime = 0.1;          // [s]
  this.OldSpeed = 0.0;
  this.GroundContactDistance = 0.0; // [m]
  this.ReverseState = 0;         // 0: before reverse; 1: Thrust to Idle; 2: Idle to ReverseIdle; 3: IdleReverse to Reverse; 4: Reverse; 5: Reverse to IdleReverse; 6: IdleReverse; 7: IdleReverse to Idle; 8: Idle
  this.ReverseStateTime = 0.0;
  this.ReverseInterrupted = 0.0; // 0..1
  this.BrakeDelay = 0.0;         // [s]
  this.AutoBrakeReleaseTime = 0.0;
  this.AutoBrakeReleaseKm = 0.0;
  this.OldAcceleration = 0.0;
}

CPlane.prototype.StartSimulation = function() {
  this.ParkingBrakeSet = false;
  this.DeltaTime = this.BrakingDeltaTime;
  this.Time = 0.0;
  this.Speed = this.Vapp - this.VFlareReduction - this.WindSpeed;  // [m/s]
  this.OldSpeed = this.Speed;
  this.Distance = 0.0;
  this.GroundContactDistance = 0.0;
  this.IsRolling = true;
  this.HasStopped = false;
  // 0: before reverse; 1: Thrust to Idle; 2: Idle to ReverseIdle; 3: IdleReverse to Reverse; 4: Reverse; 5: Reverse to IdleReverse; 6: IdleReverse; 7: IdleReverse to Idle; 8: Idle
  this.ReverseState = 0;
  this.ReverseStateTime = 0;
  if (this.Time >= this.ReverseDelay) {
    this.ReverseState = 1;
  }
  if (this.Speed <= this.ReverseSpeedLimit) {
    this.ReverseState = 1;
  }
  if (this.Speed <= this.ReverseIdleSpeed) {
    this.ReverseState = 1;
  }
  this.AutoBrakeReleaseTime = 0.0;
  // compute initial values with start conditions
  this.ComputeStaticValues();
  this.ComputeValues();
  this.Acceleration = this.ForceAcceleration / this.LandingWeight;
  this.OldAcceleration = this.Acceleration;

  this.DeltaDistance = 0.0;
  this.EnergyBrake = 0.0;
  this.EnergyRoll = 0.0;
  this.EnergyDrag = 0.0;
  this.EnergyEngines = 0.0;
  this.TotalEnergy = 0.0;
}

CPlane.prototype.StepSimulation = function() {
  var da, dv, ds;
  if (this.HasStopped) { return; }

  // compute change in acceleration and speed
  da = this.Acceleration - this.OldAcceleration;
  dv = (this.Acceleration + 0.5 * da) * this.DeltaTime;

  if (this.Speed + dv <= 0.01 && this.StopSimulationOnHalt) {
    // do not simulate damped oscillation
    this.Acceleration = 0;
    this.Speed = 0;
    dv = 0;
    da = 0;
    this.HasStopped = true;
    return;
  }

  if (this.Speed + dv <= 0.01) {
    // init damped oscillation
    var dtOld = this.DeltaTime;
    this.DeltaTime = this.OszillationDeltaTime;
    da = da / dtOld * this.DeltaTime;
    dv = (this.Acceleration + 0.5 * da) * this.DeltaTime;
  }

  // compute change in distance
  ds = (this.Speed + 0.5 * dv) * this.DeltaTime;

  // compute new current values
  this.Time += this.DeltaTime;
  this.OldSpeed = this.Speed;
  this.Speed += dv;
  this.Distance += ds;
  this.DeltaDistance = ds;
  this.ComputeValues();

  // do not track after liftoff
  if (this.ForceMainGear <= 0.0) this.Distance -= ds;

  // compute new acceleration from all forces
  this.OldAcceleration = this.Acceleration;
  this.Acceleration = this.ForceAcceleration / this.LandingWeight;

  // check stop (stop is when speed is 2 sec below Vmin)
  if (Math.abs(this.Speed) < 0.1) {
    if ((this.Time - this.V0Time > 2) || this.StopSimulationOnHalt) this.HasStopped = true;
  } else {
    this.V0Time = this.Time;
  }
}

CPlane.prototype.ComputeStaticValues = function() {

  // environment
  var temp0 = (this.Temperature+273) + this.LapseRate * this.Altitude;
  var rho0  = this.QNH * this.GasMass / (this.GasConstant * temp0);
  var base  = 1.0 - (this.LapseRate * this.Altitude / temp0);
  var expo  = (this.GasMass * this.GravityConstant / this.GasConstant / this.LapseRate) - 1.0;
  this.Rho  = rho0 * Math.pow(base, expo);

  // engine
  this.engine.Init();

  // geometry
  this.PCGx = this.PLx + (this.CG-25)*this.MAC/100.0;
  this.XL = this.PLx - this.PCGx;
  this.ZL = this.PCGz - this.PLz;
  this.XT = this.PTx - this.PCGx;
  this.ZJ = this.PCGz - this.PJz;
  this.ZM = this.PCGz;
  this.XM = this.PMx - this.PCGx;
  this.XF = this.PCGx - this.PFx;

  // some static forces
  this.ForceWeight = this.LandingWeight * this.GravityConstant * Math.cos(this.Slope);
  this.ForceSlope = this.LandingWeight * this.GravityConstant * Math.sin(this.Slope);

  // compute Trim coefficient CT
  var ft = this.ForceWeight * this.XL / (this.XL - this.XT);
  var vapp = (this.Vapp < 60) ? 60 : this.Vapp;
  this.CT = ft / (vapp * vapp);

  // Speed Constant Sp used in computation of Mue
  this.Sp = (-4.64 + 102.07 * this.MPD) / 3.6; // (/ 3.6) => km/h -> [m/s]

  // set BrakeDelay depending on AutoBrakeDelay and Spoiler extension
  if (this.AutoBrakeDelay == 0.0 && this.AutoBrake <= 1.0) {
    if (this.AutoBrake < 0.6) {
      this.BrakeDelay = this.SpoilerExtendDelay + 4;
    } else {
      this.BrakeDelay = this.SpoilerExtendDelay + 2;
    }
  } else {
    this.BrakeDelay = this.AutoBrakeDelay;
  }
}

CPlane.prototype.N1 = function( aThrust ) {
  return aThrust * (1.0 - this.engine.N1_idle) + this.engine.N1_idle;
}

CPlane.prototype.N1rev = function( aThrust ) {
  return aThrust * (this.engine.N1_maxrev - this.engine.N1_idle) + this.engine.N1_idle;
}

CPlane.prototype.ComputeValues = function() {
  var fmx, fmz, m, kf, km, ml;

  // compute TAS v and dynamic pressure q
  var v = this.Speed + this.WindSpeed;
  var q = 0.5 * this.Rho * v*v;

  var spoiler = this.SpoilerExtend * RampCosUp( this.Time, this.SpoilerExtendDelay, this.SpoilerTransTime );
  var clNoSpoiler = this.CLa * this.AoA + this.CL0 + this.CLflaps * this.FlapsExtend;

  if (v >= 0.0) {
    // wind blows from front

    // compute lift coefficient CL as f(AoA,flaps,spoiler)
    this.CL = clNoSpoiler + this.CLspoiler * spoiler;

    // compute drag coefficient CD as f(flaps,spoiler)
    this.CD = this.CD2 * clNoSpoiler*clNoSpoiler + this.CD0 + this.FlapsExtend * this.CDflaps + spoiler * this.CDspoiler;

    // compute lift, trim and drag forces and pitching moment ml as f(q,CL,CD)
    this.ForceLift = q * this.CL * this.WingArea;
    this.ForceDrag = q * this.CD * this.WingArea;
    this.ForceTrim = this.CT * v*v * RampDown( this.Time, this.SpoilerExtendDelay, this.TrimTransTime );
    ml = q * this.CM * this.MAC * this.WingArea;

  } else {
    // wind blows from behind

    // compute lift coefficient CL as f(AoA,flaps,spoiler); (spoiler has no effect on lift)
    this.CL = -clNoSpoiler;

    // compute drag coefficient CD as f(flaps,spoiler)
    this.CD = -( this.CD2 * clNoSpoiler*clNoSpoiler + this.CD0 + this.FlapsExtend * this.CDflaps + spoiler * this.CDspoiler );

    // compute lift, trim and drag forces and pitching moment ml as f(q,CL,CD)
    this.ForceLift = q * this.CL * this.WingArea;
    this.ForceDrag = q * this.CD * this.WingArea;
    this.ForceTrim = -this.CT * v*v  * RampDown( this.Time, this.SpoilerExtendDelay, this.TrimTransTime );
    ml = -( q * this.CM * this.MAC * this.WingArea );
  }

  // compute engine thrust depenging on various reverse states
  if (this.Thrust < -0.005) { // engines off
    this.ForceEngines = 0.0;
  } else {
    if (this.ReverseState == 0) {
      // before reverse
      if (this.Time < this.ReverseDelay) {
        // const thrust
        this.engine.Compute( this.N1( this.Thrust ), v );
        this.ForceEngines = this.NEngines * this.engine.F;
      } else {
        // init spool down to idle
        this.ReverseStateTime = this.ReverseDelay;
        this.ReverseState = 1;
      }
    }
    if (this.ReverseState == 1) {
      // Thrust to Idle
      var tt = this.Thrust * this.SpoolTime;
      if (this.Time < this.ReverseStateTime + tt) {
        // spooling down
        var f = RampQDown( this.Time, this.ReverseStateTime, tt );
        this.engine.Compute( this.N1( f * this.Thrust ), v );
        this.ForceEngines = this.NEngines * this.engine.F;
      } else {
        // init transition to reverse or go to state Idle
        if (this.Reverse < -0.005) {
          // no reverser used -> state 8
          this.ReverseState = 8;
        } else {
          // init transition to reverse
          this.ReverseStateTime += tt;
          this.ReverseState = 2;
        }
      }
    }
    if (this.ReverseState == 2) {
      // Idle to ReverseIdle
      if (this.Time < this.ReverseStateTime + this.ReverseTransTime) {
        // compute transition thrust
        this.engine.Compute( this.N1(0), v );
        var f = RampCosDown( this.Time, this.ReverseStateTime, this.ReverseTransTime );
        this.ForceEngines = this.NEngines * (f * this.engine.F + (1-f) * this.engine.F_rev);
      } else {
        // reverse idle reached -> hold it or apply reverse thrust or back to idle
        if (this.Speed <= this.ReverseIdleSpeed) {
          // idle speed limit exceedet -> back to idle
          this.ReverseStateTime = this.Time;
          this.ReverseState = 7;
        } else {
          if (this.Reverse == 0) {
            // hold reverse idle
            this.ReverseState = 6;
          } else {
            // init transition to reverse thrust
            this.ReverseStateTime += this.ReverseTransTime;
            this.ReverseState = 3;
          }
        }
      }
    }
    if (this.ReverseState == 3) {
      // ReverseIdle to reverse thrust
      var tt = this.Reverse * this.engine.N1_maxrev * this.SpoolTime;
      if (this.Time < this.ReverseStateTime + tt) {
        // spooling up to reverse thrust
        var f = RampQUp( this.Time, this.ReverseStateTime, tt );
        if (this.Speed <= this.ReverseSpeedLimit) {
          // reverse speed limit exceedet -> back to reverse idle
          this.ReverseInterrupted = f * this.Reverse;
          this.ReverseStateTime = this.Time;
          this.ReverseState = 5;
        } else {
          // compute current reverse thrust on spooling up
          this.engine.Compute( this.N1rev(f * this.Reverse), v );
          this.ForceEngines = this.NEngines * this.engine.F_rev;
        }
      } else {
        // reverse thrust reached -> hold it
        this.ReverseState = 4;
      }
    }
    if (this.ReverseState == 4) {
      // Reverse thrust set; monitor speed
      if (this.Speed <= this.ReverseSpeedLimit) {
        // reverse speed limit exceedet -> spool down to reverse idle
        this.ReverseStateTime = this.Time;
        this.ReverseState = 5;
      } else {
        // computing reverse force
        this.engine.Compute( this.N1rev(this.Reverse), v );
        this.ForceEngines = this.NEngines * this.engine.F_rev;
      }
    }
    if (this.ReverseState == 5) {
      // spooling down from reverse thrust to reverse idle
      var thr = this.Reverse;
      if (this.ReverseInterrupted > 0.0) thr = this.ReverseInterrupted;
      var tt = thr * this.SpoolTime * this.engine.N1_maxrev;
      if (this.Time < this.ReverseStateTime + tt) {
        // spooling down to reverse idle
        var f = RampQDown( this.Time, this.ReverseStateTime, tt );
        this.engine.Compute( this.N1rev(f * thr), v );
        this.ForceEngines = this.NEngines * this.engine.F_rev;
      } else {
        // reverse idle reached -> hold it
        this.ReverseState = 6;
        this.ReverseInterrupted = 0.0;
      }
    }
    if (this.ReverseState == 6) {
      // back to reverse idle set; monitor speed
      if (this.Speed <= this.ReverseIdleSpeed) {
        // reverse idle speed limit exceedet -> transition to idle thrust
        this.ReverseStateTime = this.Time;
        this.ReverseState = 7;
      } else {
        // hold reverse idle
        this.engine.Compute( this.N1rev(0), v );
        this.ForceEngines = this.NEngines * this.engine.F_rev;
      }
    }
    if (this.ReverseState == 7) {
      // transition from reverse idle to idle
      if (this.Time < this.ReverseStateTime + this.ReverseTransTime) {
        // compute transition thrust
        this.engine.Compute( this.N1(0), v );
        var f = RampCosUp( this.Time, this.ReverseStateTime, this.ReverseTransTime );
        this.ForceEngines = this.NEngines * (f * this.engine.F + (1-f) * this.engine.F_rev);
      } else {
        // idle thrust reached -> hold it
        this.ReverseState = 8;
      }
    }
    if (this.ReverseState == 8) {
      // hold idle thrust
      this.engine.Compute( this.N1(0), v );
      this.ForceEngines = this.NEngines * this.engine.F;
    }
  }

  // compute all forces and total moment except gear and brake forces
  fmx = this.ForceDrag + this.ForceSlope - this.ForceEngines;
  fmz = this.ForceWeight - this.ForceLift - this.ForceTrim;
  m   = this.ForceLift * this.XL + this.ForceTrim * this.XT + this.ForceDrag * this.ZL - this.ForceEngines * this.ZJ - ml;

  // compute mue
  if (this.IsRolling) {
    this.Mue = this.F60 * Math.exp( (16.667 - Math.abs(0.13*this.Speed)) / this.Sp );
  } else {
    this.Mue = this.F60 * Math.exp( 16.667 / this.Sp );
  }

  // compute Cr, assert Cr <= Mue
  this.Cr = (this.CR0 + this.CR1 * this.Speed * this.Speed / 2 / this.GravityConstant) * Math.pow( fmz, 1/3 ) * this.QNH / this.Pwheel;
  // if verry slippery, Cr = Mue
  if (this.Cr > this.Mue) this.Cr = this.Mue;

  // compute gear and brake forces
  if (fmz < 0) {

    // lift off
    this.Mue = 0.0;
    this.ForceMainGear = 0.0;
    this.ForceNoseGear = 0.0;
    this.ForceGear = 0.0;
    this.ForceMainBrake = 0.0;
    this.ForceNoseBrake = 0.0;
    this.ForceBrake = 0.0;
    this.ForceRoll = 0.0;

  } else {

    // init brake coefficients to minimum; Note: Cr <= Mue
    kf = this.Cr;
    km = this.Cr;

    if (this.Speed < 0) {
      this.ParkingBrakeSet = true;
    }

    // compute brake coefficients depending on braking action and environment conditions
    if (this.ParkingBrakeSet) {

      km = this.Mue;

    } else {

      if (this.AutoBrakeReleaseTime == 0.0) {

        // auto braking; compute km
        if (this.AutoBrake > 0.0) {

          var aReq = this.AutoBrake * this.AutoBrakeLimit;
          var fb = this.LandingWeight * aReq - fmx;

          if (fb > 0) {
            var kmReq = ( fb * ( kf * this.ZM - this.XF - this.XM ) + kf * ( m + fmz * this.XM ) ) / ( fb * this.ZM - fmz * this.XF + m );

            if (kmReq > km) {
              var b = RampCosUp( this.Time, this.BrakeDelay, this.AutoBrakeTransTime*this.AutoBrake );
              km = b * (kmReq-km) + km;
              if (km > this.Mue) { km = this.Mue; }
            }
          }
        }

        // if MaxBrakeForce is exceedet, apply MaxBrakeForce
        var fbrake = km * (fmz * (this.XF - kf * this.ZM) - m) / ((this.XM + this.XF) + this.ZM * (km - kf));
        if (fbrake > this.MaxBrakeForce) {
          var kmMax = this.Cr;
          var fb = this.MaxBrakeForce + this.Cr * fmz;
          var kmReq = ( fb * ( kf * this.ZM - this.XF - this.XM ) + kf * ( m + fmz * this.XM ) ) / ( fb * this.ZM - fmz * this.XF + m );
          if (kmReq > kmMax) {
            kmMax = kmReq;
            if (kmMax < km) { km = kmMax; }
            if (km > this.Mue) { km = this.Mue; }
          }
        }

        if (this.ManualBraking > 0.0 && this.Speed <= this.ManBrakeSpeed) {
          // trigger manual braking
          this.AutoBrakeReleaseTime = this.Time;
          this.AutoBrakeReleaseKm = km;
        }

      }

      if (this.AutoBrakeReleaseTime > 0.0) {

        // manual braking
        var fb = this.ManualBraking * this.MaxBrakeForce + this.Cr * fmz;
        var kmReq = ( fb * ( kf * this.ZM - this.XF - this.XM ) + kf * ( m + fmz * this.XM ) ) / ( fb * this.ZM - fmz * this.XF + m );
        var kmMin = km;

        if (kmReq > km) {
          var b = RampCosUp( this.Time, this.AutoBrakeReleaseTime, this.ManBrakeTransTime*this.ManualBraking+1 );
          km = b * (kmReq-km) + km;
          if (km > this.Mue) { km = this.Mue; }
        }

        var ba = RampQDown( this.Time, this.AutoBrakeReleaseTime, this.AutoBrakeTransTime );
        var kmAuto = ba * (this.AutoBrakeReleaseKm-kmMin) + kmMin;
        if (kmAuto > km) {
          km = kmAuto;
          if (km > this.Mue) { km = this.Mue; }
        }

      }

    }

    // check sticky or rolling
    if (this.IsRolling) {

      // becomes sticky, if speed changes direction
      if (this.OldSpeed*this.Speed < 0) {
        this.IsRolling = false;
        this.GroundContactDistance = this.Distance - (this.OldSpeed/Math.abs(this.OldSpeed)) * this.ForceMainBrake / this.Kf;
      }

    } else {

      var fb = this.Kf * (this.Distance - this.GroundContactDistance) + this.Kd * this.Speed;
      var fgm = (fmz * this.XF - fb * this.ZM - m) / (this.XM + this.XF);
      var fgf = fmz - fgm;
      var fbMax = km * fgm + kf * fgf;
      // becomes rolling, |fb| > fbMax
      if (Math.abs(fb) > fbMax) this.IsRolling = true;

    }

    // compute gear forces
    if (this.IsRolling) {

      this.ForceMainGear = (fmz * (this.XF - kf * this.ZM) - m) / ((this.XM + this.XF) + this.ZM * (km - kf));
      this.ForceNoseGear = fmz - this.ForceMainGear;
      this.ForceGear = this.ForceMainGear + this.ForceNoseGear;

      this.ForceMainBrake = km * this.ForceMainGear;
      this.ForceNoseBrake = kf * this.ForceNoseGear;
      if (this.Speed < 0) {
        this.ForceMainBrake = -this.ForceMainBrake;
        this.ForceNoseBrake = -this.ForceNoseBrake;
      }
      this.ForceBrake = this.ForceMainBrake + this.ForceNoseBrake;
      this.ForceRoll = this.ForceGear * kf;

      var deltaEnergy = this.DeltaDistance * this.ForceDrag;
      this.EnergyDrag += deltaEnergy;
      this.TotalEnergy += deltaEnergy;
      var deltaEnergy = this.DeltaDistance * (-this.ForceEngines);
      this.EnergyEngines += deltaEnergy;
      this.TotalEnergy += deltaEnergy;
      var deltaEnergy = this.DeltaDistance * (this.ForceBrake - this.ForceRoll);
      this.EnergyBrake += deltaEnergy;
      this.TotalEnergy += deltaEnergy;
      var deltaEnergy = this.DeltaDistance * this.ForceRoll;
      this.EnergyRoll += deltaEnergy;
      this.TotalEnergy += deltaEnergy;

    } else {

      var fb = this.Kf * (this.Distance - this.GroundContactDistance) + this.Kd * this.Speed;
      this.ForceMainGear = (fmz * this.XF - fb * this.ZM - m) / (this.XM + this.XF);
      this.ForceNoseGear = fmz - this.ForceMainGear;
      this.ForceGear = this.ForceMainGear + this.ForceNoseGear;

      var fbf = fb * this.ForceNoseGear / this.ForceGear;
      if (fbf > kf * this.ForceNoseGear) fbf = kf * this.ForceNoseGear;
      this.ForceNoseBrake = fbf;
      this.ForceMainBrake = fb - fbf;
      this.ForceBrake = fb;
      this.ForceRoll = 0.0;

    }
  }

  this.ForceAcceleration = this.ForceEngines - this.ForceDrag - this.ForceBrake - this.ForceSlope;
}

function RampUp( t, triggerTime, upDuration ) {
  if (t <= triggerTime) return 0.0;
  if (t >= triggerTime+upDuration) return 1.0;
  return (t-triggerTime)/upDuration;
}

function RampDown( t, triggerTime, downDuration ) {
  if (t <= triggerTime) return 1.0;
  if (t >= triggerTime+downDuration) return 0.0;
  return 1.0 - (t-triggerTime)/downDuration;
}

function RampQUp( t, triggerTime, upDuration ) {
  var x = RampUp( t, triggerTime, upDuration );
  return 1.0 - Math.pow( x - 1.0, 2 );
}

function RampQDown( t, triggerTime, downDuration ) {
  var x = RampUp( t, triggerTime, downDuration );
  return Math.pow( x - 1.0, 2 );
}

function RampCosUp( t, triggerTime, upDuration ) {
  var x = RampUp( t, triggerTime, upDuration );
  return 0.5 - Math.cos( x * Math.PI ) / 2.0;
}

function RampCosDown( t, triggerTime, downDuration ) {
  var x = RampUp( t, triggerTime, downDuration );
  return 0.5 + Math.cos( x * Math.PI ) / 2.0;
}


More Page Infos / Sitemap
Created Freitag, 22. Januar 2010
Scroll to Top of Page
Changed Sonntag, 27. Oktober 2019