private static void UpdateSpeeds(Train Train, double TimeElapsed)
{
if (Game.MinimalisticSimulation & Train == PlayerTrain)
{
// hold the position of the player's train during startup
for (int i = 0; i < Train.Cars.Length; i++)
{
Train.Cars[i].Specs.CurrentSpeed = 0.0;
Train.Cars[i].Specs.CurrentAccelerationOutput = 0.0;
}
return;
}
// update brake system
double[] DecelerationDueToBrake, DecelerationDueToMotor;
UpdateBrakeSystem(Train, TimeElapsed, out DecelerationDueToBrake, out DecelerationDueToMotor);
// calculate new car speeds
double[] NewSpeeds = new double[Train.Cars.Length];
for (int i = 0; i < Train.Cars.Length; i++)
{
double PowerRollingCouplerAcceleration;
// rolling on an incline
{
double a = Train.Cars[i].FrontAxle.Follower.WorldDirection.Y;
double b = Train.Cars[i].RearAxle.Follower.WorldDirection.Y;
PowerRollingCouplerAcceleration = -0.5 * (a + b) * Game.RouteAccelerationDueToGravity;
}
// friction
double FrictionBrakeAcceleration;
{
double v = Math.Abs(Train.Cars[i].Specs.CurrentSpeed);
double a = GetResistance(Train, i, ref Train.Cars[i].FrontAxle, v);
double b = GetResistance(Train, i, ref Train.Cars[i].RearAxle, v);
FrictionBrakeAcceleration = 0.5 * (a + b);
}
// power
double wheelspin = 0.0;
double wheelSlipAccelerationMotorFront;
double wheelSlipAccelerationMotorRear;
double wheelSlipAccelerationBrakeFront;
double wheelSlipAccelerationBrakeRear;
if (Train.Cars[i].Derailed)
{
wheelSlipAccelerationMotorFront = 0.0;
wheelSlipAccelerationBrakeFront = 0.0;
wheelSlipAccelerationMotorRear = 0.0;
wheelSlipAccelerationBrakeRear = 0.0;
}
else
{
wheelSlipAccelerationMotorFront = GetCriticalWheelSlipAccelerationForElectricMotor(Train, i,Train.Cars[i].FrontAxle.Follower.AdhesionMultiplier, Train.Cars[i].FrontAxle.Follower.WorldUp.Y,Train.Cars[i].Specs.CurrentSpeed);
wheelSlipAccelerationMotorRear = GetCriticalWheelSlipAccelerationForElectricMotor(Train, i,Train.Cars[i].RearAxle.Follower.AdhesionMultiplier, Train.Cars[i].RearAxle.Follower.WorldUp.Y,Train.Cars[i].Specs.CurrentSpeed);
wheelSlipAccelerationBrakeFront = GetCriticalWheelSlipAccelerationForFrictionBrake(Train, i,Train.Cars[i].FrontAxle.Follower.AdhesionMultiplier, Train.Cars[i].FrontAxle.Follower.WorldUp.Y,Train.Cars[i].Specs.CurrentSpeed);
wheelSlipAccelerationBrakeRear = GetCriticalWheelSlipAccelerationForFrictionBrake(Train, i,Train.Cars[i].RearAxle.Follower.AdhesionMultiplier, Train.Cars[i].RearAxle.Follower.WorldUp.Y,Train.Cars[i].Specs.CurrentSpeed);
}
if (DecelerationDueToMotor[i] == 0.0)
{
double a;
if (Train.Cars[i].Specs.IsMotorCar)
{
if (DecelerationDueToMotor[i] == 0.0)
{
if (Train.Specs.CurrentReverser.Actual != 0 & Train.Specs.CurrentPowerNotch.Actual > 0 & !Train.Specs.CurrentHoldBrake.Actual & !Train.Specs.CurrentEmergencyBrake.Actual)
{
// target acceleration
a = GetAccelerationOutput(Train, i, Train.Specs.CurrentPowerNotch.Actual - 1, (double)Train.Specs.CurrentReverser.Actual * Train.Cars[i].Specs.CurrentSpeed);
// readhesion device
if (a > Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput)
{
a = Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput;
}
// wheel slip
if (a < wheelSlipAccelerationMotorFront)
{
Train.Cars[i].FrontAxle.CurrentWheelSlip = false;
}
else
{
Train.Cars[i].FrontAxle.CurrentWheelSlip = true;
wheelspin += (double)Train.Specs.CurrentReverser.Actual * a * Train.Cars[i].Specs.MassCurrent;
}
if (a < wheelSlipAccelerationMotorRear)
{
Train.Cars[i].RearAxle.CurrentWheelSlip = false;
}
else
{
Train.Cars[i].RearAxle.CurrentWheelSlip = true;
wheelspin += (double)Train.Specs.CurrentReverser.Actual * a * Train.Cars[i].Specs.MassCurrent;
}
// readhesion device
{
if (Game.SecondsSinceMidnight >= Train.Cars[i].Specs.ReAdhesionDevice.NextUpdateTime)
{
double d = Train.Cars[i].Specs.ReAdhesionDevice.UpdateInterval;
double f = Train.Cars[i].Specs.ReAdhesionDevice.ApplicationFactor;
double t = Train.Cars[i].Specs.ReAdhesionDevice.ReleaseInterval;
double r = Train.Cars[i].Specs.ReAdhesionDevice.ReleaseFactor;
Train.Cars[i].Specs.ReAdhesionDevice.NextUpdateTime = Game.SecondsSinceMidnight + d;
if (Train.Cars[i].FrontAxle.CurrentWheelSlip | Train.Cars[i].RearAxle.CurrentWheelSlip)
{
Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput = a * f;
Train.Cars[i].Specs.ReAdhesionDevice.TimeStable = 0.0;
}
else
{
Train.Cars[i].Specs.ReAdhesionDevice.TimeStable += d;
if (Train.Cars[i].Specs.ReAdhesionDevice.TimeStable >= t)
{
Train.Cars[i].Specs.ReAdhesionDevice.TimeStable -= t;
if (r != 0.0 & Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput <= a + 1.0)
{
if (Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput < 0.025)
{
Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput = 0.025;
}
else
{
Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput *= r;
}
}
else
{
Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput = double.PositiveInfinity;
}
}
}
}
}
// const speed
if (Train.Specs.CurrentConstSpeed)
{
if (Game.SecondsSinceMidnight >= Train.Cars[i].Specs.ConstSpeed.NextUpdateTime)
{
Train.Cars[i].Specs.ConstSpeed.NextUpdateTime = Game.SecondsSinceMidnight + Train.Cars[i].Specs.ConstSpeed.UpdateInterval;
Train.Cars[i].Specs.ConstSpeed.CurrentAccelerationOutput -= 0.8 * Train.Cars[i].Specs.CurrentAcceleration * (double)Train.Specs.CurrentReverser.Actual;
if (Train.Cars[i].Specs.ConstSpeed.CurrentAccelerationOutput < 0.0) Train.Cars[i].Specs.ConstSpeed.CurrentAccelerationOutput = 0.0;
}
if (a > Train.Cars[i].Specs.ConstSpeed.CurrentAccelerationOutput) a = Train.Cars[i].Specs.ConstSpeed.CurrentAccelerationOutput;
if (a < 0.0) a = 0.0;
}
else
{
Train.Cars[i].Specs.ConstSpeed.CurrentAccelerationOutput = a;
}
// finalize
if (wheelspin != 0.0) a = 0.0;
}
else
{
a = 0.0;
Train.Cars[i].FrontAxle.CurrentWheelSlip = false;
Train.Cars[i].RearAxle.CurrentWheelSlip = false;
}
}
else
{
a = 0.0;
Train.Cars[i].FrontAxle.CurrentWheelSlip = false;
Train.Cars[i].RearAxle.CurrentWheelSlip = false;
}
}
else
{
a = 0.0;
Train.Cars[i].FrontAxle.CurrentWheelSlip = false;
Train.Cars[i].RearAxle.CurrentWheelSlip = false;
}
if (!Train.Cars[i].Derailed)
{
if (Train.Cars[i].Specs.CurrentAccelerationOutput < a)
{
if (Train.Cars[i].Specs.CurrentAccelerationOutput < 0.0)
{
Train.Cars[i].Specs.CurrentAccelerationOutput += Train.Cars[i].Specs.JerkBrakeDown * TimeElapsed;
}
else
{
Train.Cars[i].Specs.CurrentAccelerationOutput += Train.Cars[i].Specs.JerkPowerUp * TimeElapsed;
}
if (Train.Cars[i].Specs.CurrentAccelerationOutput > a)
{
Train.Cars[i].Specs.CurrentAccelerationOutput = a;
}
}
else
{
Train.Cars[i].Specs.CurrentAccelerationOutput -= Train.Cars[i].Specs.JerkPowerDown * TimeElapsed;
if (Train.Cars[i].Specs.CurrentAccelerationOutput < a)
{
Train.Cars[i].Specs.CurrentAccelerationOutput = a;
}
}
}
else
{
Train.Cars[i].Specs.CurrentAccelerationOutput = 0.0;
}
}
// brake
bool wheellock = wheelspin == 0.0 & Train.Cars[i].Derailed;
if (!Train.Cars[i].Derailed & wheelspin == 0.0)
{
double a;
// motor
if (Train.Cars[i].Specs.IsMotorCar & DecelerationDueToMotor[i] != 0.0)
{
a = -DecelerationDueToMotor[i];
if (Train.Cars[i].Specs.CurrentAccelerationOutput > a)
{
if (Train.Cars[i].Specs.CurrentAccelerationOutput > 0.0)
{
Train.Cars[i].Specs.CurrentAccelerationOutput -= Train.Cars[i].Specs.JerkPowerDown * TimeElapsed;
}
else
{
Train.Cars[i].Specs.CurrentAccelerationOutput -= Train.Cars[i].Specs.JerkBrakeUp * TimeElapsed;
}
if (Train.Cars[i].Specs.CurrentAccelerationOutput < a)
{
Train.Cars[i].Specs.CurrentAccelerationOutput = a;
}
}
else
{
Train.Cars[i].Specs.CurrentAccelerationOutput += Train.Cars[i].Specs.JerkBrakeDown * TimeElapsed;
if (Train.Cars[i].Specs.CurrentAccelerationOutput > a)
{
Train.Cars[i].Specs.CurrentAccelerationOutput = a;
}
}
}
// brake
a = DecelerationDueToBrake[i];
if (Train.Cars[i].Specs.CurrentSpeed >= -0.01 & Train.Cars[i].Specs.CurrentSpeed <= 0.01)
{
double rf = Train.Cars[i].FrontAxle.Follower.WorldDirection.Y;
double rr = Train.Cars[i].RearAxle.Follower.WorldDirection.Y;
double ra = Math.Abs(0.5 * (rf + rr) * Game.RouteAccelerationDueToGravity);
if (a > ra) a = ra;
}
double factor = Train.Cars[i].Specs.MassEmpty / Train.Cars[i].Specs.MassCurrent;
if (a >= wheelSlipAccelerationBrakeFront)
{
wheellock = true;
}
else
{
FrictionBrakeAcceleration += 0.5 * a * factor;
}
if (a >= wheelSlipAccelerationBrakeRear)
{
wheellock = true;
}
else
{
FrictionBrakeAcceleration += 0.5 * a * factor;
}
}
else if (Train.Cars[i].Derailed)
{
FrictionBrakeAcceleration += Game.CoefficientOfGroundFriction * Game.RouteAccelerationDueToGravity;
}
// motor
if (Train.Specs.CurrentReverser.Actual != 0)
{
double factor = Train.Cars[i].Specs.MassEmpty / Train.Cars[i].Specs.MassCurrent;
if (Train.Cars[i].Specs.CurrentAccelerationOutput > 0.0)
{
PowerRollingCouplerAcceleration += (double)Train.Specs.CurrentReverser.Actual * Train.Cars[i].Specs.CurrentAccelerationOutput * factor;
}
else
{
double a = -Train.Cars[i].Specs.CurrentAccelerationOutput;
if (a >= wheelSlipAccelerationMotorFront)
{
Train.Cars[i].FrontAxle.CurrentWheelSlip = true;
}
else if (!Train.Cars[i].Derailed)
{
FrictionBrakeAcceleration += 0.5 * a * factor;
}
if (a >= wheelSlipAccelerationMotorRear)
{
Train.Cars[i].RearAxle.CurrentWheelSlip = true;
}
else
{
FrictionBrakeAcceleration += 0.5 * a * factor;
}
}
}
else
{
Train.Cars[i].Specs.CurrentAccelerationOutput = 0.0;
}
// perceived speed
{
double target;
if (wheellock)
{
target = 0.0;
}
else if (wheelspin == 0.0)
{
target = Train.Cars[i].Specs.CurrentSpeed;
}
else
{
target = Train.Cars[i].Specs.CurrentSpeed + wheelspin / 2500.0;
}
double diff = target - Train.Cars[i].Specs.CurrentPerceivedSpeed;
double rate = (diff < 0.0 ? 5.0 : 1.0) * Game.RouteAccelerationDueToGravity * TimeElapsed;
rate *= 1.0 - 0.7 / (diff * diff + 1.0);
double factor = rate * rate;
factor = 1.0 - factor / (factor + 1000.0);
rate *= factor;
if (diff >= -rate & diff <= rate)
{
Train.Cars[i].Specs.CurrentPerceivedSpeed = target;
}
else
{
Train.Cars[i].Specs.CurrentPerceivedSpeed += rate * (double)Math.Sign(diff);
}
}
// perceived traveled distance
Train.Cars[i].Specs.CurrentPerceivedTraveledDistance += Math.Abs(Train.Cars[i].Specs.CurrentPerceivedSpeed) * TimeElapsed;
// calculate new speed
{
int d = Math.Sign(Train.Cars[i].Specs.CurrentSpeed);
double a = PowerRollingCouplerAcceleration;
double b = FrictionBrakeAcceleration;
if (Math.Abs(a) < b)
{
if (Math.Sign(a) == d)
{
if (d == 0)
{
NewSpeeds[i] = 0.0;
}
else
{
double c = (b - Math.Abs(a)) * TimeElapsed;
if (Math.Abs(Train.Cars[i].Specs.CurrentSpeed) > c)
{
NewSpeeds[i] = Train.Cars[i].Specs.CurrentSpeed - (double)d * c;
}
else
{
NewSpeeds[i] = 0.0;
}
}
}
else
{
double c = (Math.Abs(a) + b) * TimeElapsed;
if (Math.Abs(Train.Cars[i].Specs.CurrentSpeed) > c)
{
NewSpeeds[i] = Train.Cars[i].Specs.CurrentSpeed - (double)d * c;
}
else
{
NewSpeeds[i] = 0.0;
}
}
}
else
{
NewSpeeds[i] = Train.Cars[i].Specs.CurrentSpeed + (a - b * (double)d) * TimeElapsed;
}
}
}
// calculate center of mass position
double[] CenterOfCarPositions = new double[Train.Cars.Length];
double CenterOfMassPosition = 0.0;
double TrainMass = 0.0;
for (int i = 0; i < Train.Cars.Length; i++)
{
double pr = Train.Cars[i].RearAxle.Follower.TrackPosition - Train.Cars[i].RearAxlePosition;
double pf = Train.Cars[i].FrontAxle.Follower.TrackPosition - Train.Cars[i].FrontAxlePosition;
CenterOfCarPositions[i] = 0.5 * (pr + pf);
CenterOfMassPosition += CenterOfCarPositions[i] * Train.Cars[i].Specs.MassCurrent;
TrainMass += Train.Cars[i].Specs.MassCurrent;
}
if (TrainMass != 0.0)
{
CenterOfMassPosition /= TrainMass;
}
{ // coupler
// determine closest cars
int p = -1; // primary car index
int s = -1; // secondary car index
{
double PrimaryDistance = double.MaxValue;
for (int i = 0; i < Train.Cars.Length; i++)
{
double d = Math.Abs(CenterOfCarPositions[i] - CenterOfMassPosition);
if (d < PrimaryDistance)
{
PrimaryDistance = d;
p = i;
}
}
double SecondDistance = double.MaxValue;
for (int i = p - 1; i <= p + 1; i++)
{
if (i >= 0 & i < Train.Cars.Length & i != p)
{
double d = Math.Abs(CenterOfCarPositions[i] - CenterOfMassPosition);
if (d < SecondDistance)
{
SecondDistance = d;
s = i;
}
}
}
if (s >= 0 && PrimaryDistance <= 0.25 * (PrimaryDistance + SecondDistance))
{
s = -1;
}
}
// coupler
bool[] CouplerCollision = new bool[Train.Couplers.Length];
int cf, cr;
if (s >= 0)
{
// use two cars as center of mass
if (p > s)
{
int t = p; p = s; s = t;
}
double min = Train.Couplers[p].MinimumDistanceBetweenCars;
double max = Train.Couplers[p].MaximumDistanceBetweenCars;
double d = CenterOfCarPositions[p] - CenterOfCarPositions[s] - 0.5 * (Train.Cars[p].Length + Train.Cars[s].Length);
if (d < min)
{
double t = (min - d) / (Train.Cars[p].Specs.MassCurrent + Train.Cars[s].Specs.MassCurrent);
double tp = t * Train.Cars[s].Specs.MassCurrent;
double ts = t * Train.Cars[p].Specs.MassCurrent;
TrackManager.UpdateCarFollowers(ref Train.Cars[p], tp, false, false);
TrackManager.UpdateCarFollowers(ref Train.Cars[s], -ts, false, false);
CenterOfCarPositions[p] += tp;
CenterOfCarPositions[s] -= ts;
CouplerCollision[p] = true;
}
else if (d > max & !Train.Cars[p].Derailed & !Train.Cars[s].Derailed)
{
double t = (d - max) / (Train.Cars[p].Specs.MassCurrent + Train.Cars[s].Specs.MassCurrent);
double tp = t * Train.Cars[s].Specs.MassCurrent;
double ts = t * Train.Cars[p].Specs.MassCurrent;
TrackManager.UpdateCarFollowers(ref Train.Cars[p], -tp, false, false);
TrackManager.UpdateCarFollowers(ref Train.Cars[s], ts, false, false);
CenterOfCarPositions[p] -= tp;
CenterOfCarPositions[s] += ts;
CouplerCollision[p] = true;
}
cf = p;
cr = s;
}
else
{
// use one car as center of mass
cf = p;
cr = p;
}
// front cars
for (int i = cf - 1; i >= 0; i--)
{
double min = Train.Couplers[i].MinimumDistanceBetweenCars;
double max = Train.Couplers[i].MaximumDistanceBetweenCars;
double d = CenterOfCarPositions[i] - CenterOfCarPositions[i + 1] - 0.5 * (Train.Cars[i].Length + Train.Cars[i + 1].Length);
if (d < min)
{
double t = min - d + 0.0001;
TrackManager.UpdateCarFollowers(ref Train.Cars[i], t, false, false);
CenterOfCarPositions[i] += t;
CouplerCollision[i] = true;
}
else if (d > max & !Train.Cars[i].Derailed & !Train.Cars[i + 1].Derailed)
{
double t = d - max + 0.0001;
TrackManager.UpdateCarFollowers(ref Train.Cars[i], -t, false, false);
CenterOfCarPositions[i] -= t;
CouplerCollision[i] = true;
}
}
// rear cars
for (int i = cr + 1; i < Train.Cars.Length; i++)
{
double min = Train.Couplers[i - 1].MinimumDistanceBetweenCars;
double max = Train.Couplers[i - 1].MaximumDistanceBetweenCars;
double d = CenterOfCarPositions[i - 1] - CenterOfCarPositions[i] - 0.5 * (Train.Cars[i].Length + Train.Cars[i - 1].Length);
if (d < min)
{
double t = min - d + 0.0001;
TrackManager.UpdateCarFollowers(ref Train.Cars[i], -t, false, false);
CenterOfCarPositions[i] -= t;
CouplerCollision[i - 1] = true;
}
else if (d > max & !Train.Cars[i].Derailed & !Train.Cars[i - 1].Derailed)
{
double t = d - max + 0.0001;
TrackManager.UpdateCarFollowers(ref Train.Cars[i], t, false, false);
CenterOfCarPositions[i] += t;
CouplerCollision[i - 1] = true;
}
}
// update speeds
for (int i = 0; i < Train.Couplers.Length; i++)
{
if (CouplerCollision[i])
{
int j;
for (j = i + 1; j < Train.Couplers.Length; j++)
{
if (!CouplerCollision[j])
{
break;
}
}
double v = 0.0;
double m = 0.0;
for (int k = i; k <= j; k++)
{
v += NewSpeeds[k] * Train.Cars[k].Specs.MassCurrent;
m += Train.Cars[k].Specs.MassCurrent;
}
if (m != 0.0)
{
v /= m;
}
for (int k = i; k <= j; k++)
{
if (Interface.CurrentOptions.Derailments && Math.Abs(v - NewSpeeds[k]) > 0.5 * Game.CriticalCollisionSpeedDifference)
{
Train.Derail(k, TimeElapsed);
}
NewSpeeds[k] = v;
}
i = j - 1;
}
}
}
// update average data
Train.Specs.CurrentAverageSpeed = 0.0;
Train.Specs.CurrentAverageAcceleration = 0.0;
Train.Specs.CurrentAverageJerk = 0.0;
double invtime = TimeElapsed != 0.0 ? 1.0 / TimeElapsed : 1.0;
for (int i = 0; i < Train.Cars.Length; i++)
{
Train.Cars[i].Specs.CurrentAcceleration = (NewSpeeds[i] - Train.Cars[i].Specs.CurrentSpeed) * invtime;
Train.Cars[i].Specs.CurrentSpeed = NewSpeeds[i];
Train.Specs.CurrentAverageSpeed += NewSpeeds[i];
Train.Specs.CurrentAverageAcceleration += Train.Cars[i].Specs.CurrentAcceleration;
}
double invcarlen = 1.0 / (double)Train.Cars.Length;
Train.Specs.CurrentAverageSpeed *= invcarlen;
Train.Specs.CurrentAverageAcceleration *= invcarlen;
}