Skip to content

Commit a894670

Browse files
authored
Merge pull request #627 from cesarBLG/enhance-adhesion
Improve advanced adhesion model
2 parents d56c11a + eca1e35 commit a894670

File tree

12 files changed

+446
-625
lines changed

12 files changed

+446
-625
lines changed

Source/Documentation/Manual/physics.rst

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -313,31 +313,33 @@ the *Axle brake force*. The *Axle out force* is the output force of
313313
the adhesion model (used to pull the train). To compute the model
314314
correctly the FPS rate needs to be divided by a *Solver dividing* value
315315
in a range from 1 to 50. By default, the Runge-Kutta4 solver is used to
316-
obtain the best results. When the *Solver dividing* value is higher than
317-
40, in order to reduce CPU load the Euler-modified solver is used instead.
316+
obtain the best results.
318317

319318
In some cases when the CPU load is high, the time step for the computation
320319
may become very high and the simulation may start to oscillate (the
321-
*Wheel slip* rate of change (in the brackets) becomes very high). There
322-
is a stability correction feature that modifies the dynamics of the
323-
adhesion characteristics. Higher instability can cause a huge wheel slip.
320+
*Wheel slip* rate of change (in the brackets) becomes very high).
324321
You can use the ``DebugResetWheelSlip`` (``<Ctrl+X>`` keys by default)
325322
command to reset the adhesion model. If you experience such behavior most
326323
of time, use the basic adhesion model instead by pressing
327324
``DebugToggleAdvancedAdhesion`` ( ``<Ctrl+Alt+X>`` keys by default).
328325

329-
Another option is to use a Moving average filter available in the
330-
:ref:`Simulation Options <options-simulation>`. The higher the value,
331-
the more stable the simulation will be. However, the higher value causes
332-
slower dynamic response. The recommended range is between 10 and 50.
333-
334326
.. index::
335327
single: ORTSWheelSlipCausesThrottleDown
336328

337329
To match some of the real world features, the *Wheel slip* event can
338330
cause automatic zero throttle setting. Use the ``Engine (ORTS
339331
(ORTSWheelSlipCausesThrottleDown))`` Boolean value of the ENG file.
340332

333+
.. index::
334+
single: ORTSSlipControlSystem
335+
336+
Modern locomotives have slip control systems which automatically adjust
337+
power, providing an optimal tractive effort avoiding wheel slip.
338+
The ``ORTSSlipControlSystem ( Full )`` parameter can be inserted
339+
into the engine section of the .eng file to indicate the presence of
340+
such system.
341+
342+
341343
Engine -- Classes of Motive Power
342344
=================================
343345

@@ -940,6 +942,20 @@ auxiliary systems can be adjusted by the optional parameter
940942
A :ref:`scripting interface <features-scripting-powersupply>` to customize the
941943
behavior of the power supply is also available.
942944

945+
Traction motor type
946+
'''''''''''''''''''
947+
948+
.. index::
949+
single: ORTSTractionMotorType
950+
951+
There are different types of electric motors: series DC motors,
952+
asynchronous/synchronous AC motors, etc.
953+
Currently a simple AC induction motor has been implemented, and can be selected
954+
with the ``ORTSTractionMotorType ( AC ) `` parameter, to be inserted in the Engine
955+
section of the ENG file. The use of this motor will have an impact on wheel slip,
956+
because the wheel speed never exceeds the frequency of the rotating magnetic field.
957+
958+
943959
Steam Locomotives
944960
-----------------
945961

Source/ORTS.Common/Integrator.cs

Lines changed: 13 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ public void Reset()
194194
/// <param name="timeSpan">Integration step or timespan in seconds</param>
195195
/// <param name="value">Value to integrate</param>
196196
/// <returns>Value of integration in the next step (t + timeSpan)</returns>
197-
public float Integrate(float timeSpan, float value)
197+
public float Integrate(float timeSpan, Func<float, float> value)
198198
{
199199
float step = 0.0f;
200200
float end = timeSpan;
@@ -207,43 +207,28 @@ public float Integrate(float timeSpan, float value)
207207
{
208208
return integralValue;
209209
}
210-
211-
//if (timeSpan > MinStep)
212210
if (Math.Abs(prevDerivation) > Error)
213211
{
214-
//count = 2 * Convert.ToInt32(Math.Round((timeSpan) / MinStep, 0));
215212
count = ++numOfSubstepsPS;
216213
if (count > MaxSubsteps)
217214
count = MaxSubsteps;
218215
waitBeforeSpeedingUp = 100;
219-
//if (numOfSubstepsPS > (MaxSubsteps / 2))
220-
// Method = IntegratorMethods.EulerBackMod;
221-
//else
222-
// Method = IntegratorMethods.RungeKutta4;
223216
}
224217
else
225218
{
226219
if (--waitBeforeSpeedingUp <= 0) //wait for a while before speeding up the integration
227220
{
228-
count = --numOfSubstepsPS;
229-
if (count < 1)
230-
count = 1;
221+
count = Math.Max(--numOfSubstepsPS, 1);
231222

232223
waitBeforeSpeedingUp = 10; //not so fast ;)
233224
}
234225
else
235226
count = numOfSubstepsPS;
236-
//IsStepDividing = false;
237227
}
238228

239229
timeSpan = timeSpan / (float)count;
240-
IsStepDividing = true;
241230
numOfSubstepsPS = count;
242-
243-
if (count > 1)
244-
IsStepDividing = true;
245-
else
246-
IsStepDividing = false;
231+
IsStepDividing = count > 1;
247232

248233

249234
#region SOLVERS
@@ -253,28 +238,26 @@ public float Integrate(float timeSpan, float value)
253238
switch (Method)
254239
{
255240
case IntegratorMethods.EulerBackward:
256-
integralValue += (derivation = timeSpan * value);
241+
integralValue += (derivation = timeSpan * value(integralValue));
257242
break;
258243
case IntegratorMethods.EulerBackMod:
259-
integralValue += (derivation = timeSpan / 2.0f * (previousValues[0] + value));
260-
previousValues[0] = value;
244+
integralValue += (derivation = timeSpan / 2.0f * (previousValues[0] + value(integralValue)));
245+
previousValues[0] = value(integralValue);
261246
break;
262247
case IntegratorMethods.EulerForward:
263248
throw new NotImplementedException("Not implemented yet!");
264249

265250
case IntegratorMethods.RungeKutta2:
266-
//throw new NotImplementedException("Not implemented yet!");
267-
k1 = integralValue + timeSpan / 2 * value;
251+
k1 = integralValue + timeSpan / 2 * value(integralValue);
268252
k2 = 2 * (k1 - integralValue) / timeSpan;
269253
integralValue += (derivation = timeSpan * k2);
270254
break;
271255
case IntegratorMethods.RungeKutta4:
272-
//throw new NotImplementedException("Not implemented yet!");
273-
k1 = timeSpan * value;
274-
k2 = k1 + timeSpan / 2.0f * value;
275-
k3 = k1 + timeSpan / 2.0f * k2;
276-
k4 = timeSpan * k3;
277-
integralValue += (derivation = (k1 + 2.0f * k2 + 2.0f * k3 + k4) / 6.0f);
256+
k1 = value(integralValue);
257+
k2 = value(integralValue + k1 * timeSpan / 2.0f);
258+
k3 = value(integralValue + k2 * timeSpan / 2.0f);
259+
k4 = value(integralValue + k3 * timeSpan);
260+
integralValue += (derivation = (k1 + 2.0f * k2 + 2.0f * k3 + k4) * timeSpan / 6.0f);
278261
break;
279262
case IntegratorMethods.NewtonRhapson:
280263
throw new NotImplementedException("Not implemented yet!");
@@ -289,7 +272,7 @@ public float Integrate(float timeSpan, float value)
289272
previousStep[i] = previousStep[i - 1];
290273
previousValues[i] = previousValues[i - 1];
291274
}
292-
previousValues[0] = value;
275+
previousValues[0] = value(integralValue);
293276
previousStep[0] = timeSpan;
294277
break;
295278
default:
@@ -313,24 +296,6 @@ public float Integrate(float timeSpan, float value)
313296
else
314297
return integralValue;
315298
}
316-
/// <summary>
317-
/// Integrates given value in time. TimeSpan (integration step) is computed internally.
318-
/// </summary>
319-
/// <param name="clockSeconds">Time value in seconds</param>
320-
/// <param name="value">Value to integrate</param>
321-
/// <returns>Value of integration in elapsedClockSeconds time</returns>
322-
public float TimeIntegrate(float clockSeconds, float value)
323-
{
324-
float timeSpan = clockSeconds - oldTime;
325-
oldTime = clockSeconds;
326-
integralValue += timeSpan * value;
327-
if (isLimited)
328-
{
329-
return (integralValue <= min) ? min : ((integralValue >= max) ? max : integralValue);
330-
}
331-
else
332-
return integralValue;
333-
}
334299

335300
public void Save(BinaryWriter outf)
336301
{

Source/Orts.Simulation/Orts.Simulation.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@
195195
<Compile Include="Simulation\Turntables.cs" />
196196
<Compile Include="Simulation\Weather.cs" />
197197
<Compile Include="Common\Scripting\EuropeanTrainControlSystem.cs" />
198+
<Compile Include="Simulation\RollingStocks\SubSystems\PowerTransmissions\InductionMotor.cs" />
198199
</ItemGroup>
199200
<ItemGroup>
200201
<ProjectReference Include="..\ORTS.Common\ORTS.Common.csproj">

Source/Orts.Simulation/Simulation/AIs/AITrain.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6298,8 +6298,6 @@ public bool SwitchToPlayerControl()
62986298
{
62996299
var loco = car as MSTSLocomotive;
63006300
loco.LocomotiveAxle.Reset(Simulator.GameTime, SpeedMpS);
6301-
loco.LocomotiveAxle.AxleSpeedMpS = SpeedMpS;
6302-
loco.LocomotiveAxle.FilterMovingAverage.Initialize(loco.AverageForceN);
63036301
loco.AntiSlip = false; // <CSComment> TODO Temporary patch until AntiSlip is re-implemented
63046302
}
63056303
if (car == Simulator.PlayerLocomotive) { leadLocomotiveIndex = j; }

Source/Orts.Simulation/Simulation/RollingStocks/MSTSDieselLocomotive.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,8 @@ public override void LoadFromWagFile(string wagFilePath)
397397
var dropoffspeed = calculatedmaximumpowerw / (MaxForceN);
398398
var configuredadhesiondropoffspeed = (Curtius_KnifflerA / (dropoffspeed + Curtius_KnifflerB) + Curtius_KnifflerC);
399399

400+
Trace.TraceInformation("Slip control system: {0}, Traction motor type: {1}", SlipControlSystem.ToString(), TractionMotorType.ToString()); // Slip control
401+
400402
Trace.TraceInformation("Apparent (Design) Adhesion: Zero - {0:N2} @ {1}, Max Continuous Speed - {2:N2} @ {3}, Drive Wheel Weight - {4}", designadhesionzerospeed, FormatStrings.FormatSpeedDisplay(zerospeed, IsMetric), designadhesionmaxcontspeed, FormatStrings.FormatSpeedDisplay(SpeedOfMaxContinuousForceMpS, IsMetric), FormatStrings.FormatMass(DrvWheelWeightKg, IsMetric));
401403
Trace.TraceInformation("OR Calculated Adhesion Setting: Zero Speed - {0:N2} @ {1}, Dropoff Speed - {2:N2} @ {3}, Max Continuous Speed - {4:N2} @ {5}", configuredadhesionzerospeed, FormatStrings.FormatSpeedDisplay(zerospeed, IsMetric), configuredadhesiondropoffspeed, FormatStrings.FormatSpeedDisplay(dropoffspeed, IsMetric), configuredadhesionmaxcontinuousspeed, FormatStrings.FormatSpeedDisplay(SpeedOfMaxContinuousForceMpS, IsMetric));
402404
}
@@ -669,13 +671,25 @@ protected override void UpdateTractiveForce(float elapsedClockSeconds, float t,
669671
// its impact. More modern locomotive have a more sophisticated system that eliminates slip in the majority (if not all circumstances).
670672
// Simple adhesion control does not have any slip control feature built into it.
671673
// TODO - a full review of slip/no slip control.
672-
if (WheelSlip && AdvancedAdhesionModel)
674+
if (TractionMotorType == TractionMotorTypes.AC)
673675
{
674-
AbsTractionSpeedMpS = AbsWheelSpeedMpS;
676+
AbsTractionSpeedMpS = AbsSpeedMpS;
677+
if (AbsWheelSpeedMpS > 1.1 * MaxSpeedMpS)
678+
{
679+
AverageForceN = TractiveForceN = 0;
680+
return;
681+
}
675682
}
676683
else
677684
{
678-
AbsTractionSpeedMpS = AbsSpeedMpS;
685+
if (WheelSlip && AdvancedAdhesionModel)
686+
{
687+
AbsTractionSpeedMpS = AbsWheelSpeedMpS;
688+
}
689+
else
690+
{
691+
AbsTractionSpeedMpS = AbsSpeedMpS;
692+
}
679693
}
680694

681695
if (TractiveForceCurves == null)

0 commit comments

Comments
 (0)