// 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; }