@@ -77,11 +77,7 @@ public class Axle
7777 /// <summary>
7878 /// Axle drive type covered by DriveType interface
7979 /// </summary>
80- protected AxleDriveType driveType ;
81- /// <summary>
82- /// Read/Write Axle drive type flag
83- /// </summary>
84- public AxleDriveType DriveType { set { driveType = value ; } get { return driveType ; } }
80+ public AxleDriveType DriveType ;
8581
8682 /// <summary>
8783 /// Axle drive represented by a motor, covered by ElectricMotor interface
@@ -96,17 +92,12 @@ public ElectricMotor Motor
9692 set
9793 {
9894 motor = value ;
99- switch ( driveType )
95+ DriveType = motor != null ? AxleDriveType . MotorDriven : AxleDriveType . ForceDriven ;
96+ switch ( DriveType )
10097 {
101- case AxleDriveType . NotDriven :
102- break ;
10398 case AxleDriveType . MotorDriven :
104- //Total inertia considering gearbox
10599 totalInertiaKgm2 = inertiaKgm2 + transmissionRatio * transmissionRatio * motor . InertiaKgm2 ;
106100 break ;
107- case AxleDriveType . ForceDriven :
108- totalInertiaKgm2 = inertiaKgm2 ;
109- break ;
110101 default :
111102 totalInertiaKgm2 = inertiaKgm2 ;
112103 break ;
@@ -149,7 +140,7 @@ public float InertiaKgm2
149140 if ( value <= 0.0 )
150141 throw new NotSupportedException ( "Inertia must be greater than zero" ) ;
151142 inertiaKgm2 = value ;
152- switch ( driveType )
143+ switch ( DriveType )
153144 {
154145 case AxleDriveType . NotDriven :
155146 break ;
@@ -398,54 +389,8 @@ public Axle()
398389 {
399390 transmissionEfficiency = 0.99f ;
400391 SlipWarningTresholdPercent = 70.0f ;
401- driveType = AxleDriveType . ForceDriven ;
402-
403- switch ( driveType )
404- {
405- case AxleDriveType . NotDriven :
406- break ;
407- case AxleDriveType . MotorDriven :
408- totalInertiaKgm2 = inertiaKgm2 + transmissionRatio * transmissionRatio * motor . InertiaKgm2 ;
409- break ;
410- case AxleDriveType . ForceDriven :
411- totalInertiaKgm2 = inertiaKgm2 ;
412- break ;
413- default :
414- totalInertiaKgm2 = inertiaKgm2 ;
415- break ;
416- }
417- }
418-
419- /// <summary>
420- /// Creates motor driven axle class instance
421- /// - sets TransmissionEfficiency to 0.99 (99%)
422- /// - sets SlipWarningThresholdPercent to 70%
423- /// - sets axle DriveType to MotorDriven
424- /// - updates totalInertiaKgm2 parameter
425- /// </summary>
426- /// <param name="electricMotor">Electric motor connected with the axle</param>
427- public Axle ( ElectricMotor electricMotor )
428- {
429- motor = electricMotor ;
430- motor . AxleConnected = this ;
431- transmissionEfficiency = 0.99f ;
432- driveType = AxleDriveType . MotorDriven ;
433-
434- switch ( driveType )
435- {
436- case AxleDriveType . NotDriven :
437- totalInertiaKgm2 = inertiaKgm2 ;
438- break ;
439- case AxleDriveType . MotorDriven :
440- totalInertiaKgm2 = inertiaKgm2 + transmissionRatio * transmissionRatio * motor . InertiaKgm2 ;
441- break ;
442- case AxleDriveType . ForceDriven :
443- totalInertiaKgm2 = inertiaKgm2 ;
444- break ;
445- default :
446- totalInertiaKgm2 = inertiaKgm2 ;
447- break ;
448- }
392+ DriveType = AxleDriveType . ForceDriven ;
393+ totalInertiaKgm2 = inertiaKgm2 ;
449394 }
450395
451396 /// <summary>
@@ -478,10 +423,10 @@ public void Save(BinaryWriter outf)
478423 float axleForceN = AxleWeightN * SlipCharacteristics ( axleSpeedMpS - TrainSpeedMpS , TrainSpeedMpS , AdhesionK , AdhesionLimit ) ;
479424
480425 float motiveAxleForceN = - axleForceN - dampingNs * ( axleSpeedMpS - TrainSpeedMpS ) ; // Force transmitted to rail + heat losses
481- if ( driveType == AxleDriveType . ForceDriven )
482- motiveAxleForceN += driveForceN * transmissionEfficiency ;
483- else if ( driveType == AxleDriveType . MotorDriven )
484- motiveAxleForceN += motor . DevelopedTorqueNm * transmissionEfficiency * WheelRadiusM ;
426+ if ( DriveType == AxleDriveType . ForceDriven )
427+ motiveAxleForceN += DriveForceN * transmissionEfficiency ;
428+ else if ( DriveType == AxleDriveType . MotorDriven )
429+ motiveAxleForceN += motor . GetDevelopedTorqueNm ( axleSpeedMpS * transmissionRatio / WheelRadiusM ) * transmissionEfficiency / WheelRadiusM ;
485430
486431 // Dissipative forces: they will never increase wheel speed
487432 float frictionalForceN = BrakeRetardForceN + frictionN ;
@@ -501,12 +446,17 @@ public void Save(BinaryWriter outf)
501446 return ( totalAxleForceN * forceToAccelerationFactor , axleSpeedMpS / WheelRadiusM , axleForceN ) ;
502447 }
503448
449+ /// <summary>
450+ /// Integrates the wheel rotation movement using a RK4 method,
451+ /// calculating the required number of substeps
452+ /// Outputs: wheel speed, wheel angular position and motive force
453+ /// </summary>
504454 void Integrate ( float elapsedClockSeconds )
505455 {
506456 if ( elapsedClockSeconds <= 0 ) return ;
507457 float prevSpeedMpS = AxleSpeedMpS ;
508458
509- if ( Math . Abs ( integratorError ) > Math . Max ( SlipSpeedMpS , 1 ) / 1000 )
459+ if ( Math . Abs ( integratorError ) > Math . Max ( ( Math . Abs ( SlipSpeedMpS ) - 1 ) * 0.01f , 0.001f ) )
510460 {
511461 ++ NumOfSubstepsPS ;
512462 waitBeforeSpeedingUp = 100 ;
@@ -527,7 +477,7 @@ void Integrate(float elapsedClockSeconds)
527477 for ( int i = 0 ; i < NumOfSubstepsPS ; i ++ )
528478 {
529479 var k1 = GetAxleMotionVariation ( AxleSpeedMpS ) ;
530- if ( i == 0 && k1 . Item1 * dt > Math . Max ( SlipSpeedMpS , 1 ) / 100 )
480+ if ( i == 0 && k1 . Item1 * dt > Math . Max ( ( Math . Abs ( SlipSpeedMpS ) - 1 ) * 10 , 1 ) / 100 )
531481 {
532482 NumOfSubstepsPS = Math . Min ( NumOfSubstepsPS + 5 , 50 ) ;
533483 dt = elapsedClockSeconds / NumOfSubstepsPS ;
@@ -549,6 +499,37 @@ void Integrate(float elapsedClockSeconds)
549499 }
550500 }
551501
502+ /// <summary>
503+ /// Work in progress. Calculates wheel creep assuming that wheel inertia
504+ /// is low, removing the need of an integrator
505+ /// Useful for slow CPUs
506+ /// </summary>
507+ void StationaryCalculation ( float elapsedClockSeconds )
508+ {
509+ var res = GetAxleMotionVariation ( AxleSpeedMpS ) ;
510+ float force = res . Item1 / forceToAccelerationFactor + res . Item3 ;
511+ float maxAdhesiveForce = AxleWeightN * AdhesionLimit ;
512+ if ( maxAdhesiveForce == 0 ) return ;
513+ float forceRatio = force / maxAdhesiveForce ;
514+ float absForceRatio = Math . Abs ( forceRatio ) ;
515+ float characteristicTime = WheelSlipThresholdMpS / ( maxAdhesiveForce * forceToAccelerationFactor ) ;
516+ if ( absForceRatio > 1 || IsWheelSlip || Math . Abs ( res . Item1 * elapsedClockSeconds ) < WheelSlipThresholdMpS )
517+ {
518+ Integrate ( elapsedClockSeconds ) ;
519+ return ;
520+ }
521+ NumOfSubstepsPS = 1 ;
522+ if ( absForceRatio < 0.001f )
523+ {
524+ AxleSpeedMpS = TrainSpeedMpS ;
525+ AxleForceN = 0 ;
526+ return ;
527+ }
528+ float x = ( float ) ( ( 1 - Math . Sqrt ( 1 - forceRatio * forceRatio ) ) / forceRatio ) ;
529+ AxleSpeedMpS = TrainSpeedMpS + MpS . FromKpH ( AdhesionK * x / AdhesionLimit ) ;
530+ AxleForceN = ( force + res . Item3 ) / 2 ;
531+ }
532+
552533 /// <summary>
553534 /// Main Update method
554535 /// - computes slip characteristics to get new axle force
@@ -570,11 +551,8 @@ public virtual void Update(float timeSpan)
570551 CompensatedAxleForceN = AxleForceN + Math . Sign ( TrainSpeedMpS ) * BrakeRetardForceN ;
571552 if ( AxleForceN == 0 ) CompensatedAxleForceN = 0 ;
572553
573- if ( driveType == AxleDriveType . MotorDriven )
574- {
575- motor . RevolutionsRad = AxleSpeedMpS * transmissionRatio / WheelRadiusM ;
576- motor . Update ( timeSpan ) ;
577- }
554+ motor ? . Update ( timeSpan ) ;
555+
578556 if ( timeSpan > 0.0f )
579557 {
580558 slipDerivationMpSS = ( SlipSpeedMpS - previousSlipSpeedMpS ) / timeSpan ;
@@ -591,8 +569,7 @@ public virtual void Update(float timeSpan)
591569 public void Reset ( )
592570 {
593571 AxleSpeedMpS = 0 ;
594- if ( motor != null )
595- motor . Reset ( ) ;
572+ motor ? . Reset ( ) ;
596573
597574 }
598575
@@ -604,8 +581,7 @@ public void Reset(double resetTime, float initValue)
604581 {
605582 AxleSpeedMpS = initValue ;
606583 ResetTime = resetTime ;
607- if ( motor != null )
608- motor . Reset ( ) ;
584+ motor ? . Reset ( ) ;
609585 }
610586
611587 /// <summary>
@@ -619,11 +595,12 @@ public void Reset(double resetTime, float initValue)
619595 /// 2*K*umax^2*dV
620596 /// u = ---------------------
621597 /// umax^2*dv^2 + K^2
622- /// For high slip speeds (after the inflexion point of u), the formula is
623- /// replaced with an exponentially decaying function (with smooth coupling)
624- /// reaching a 40% of maximum adhesion at infinity. Quick fix until
625- /// further investigation is done to get a single formula that provides
626- /// non zero adhesion at infinity.
598+ ///
599+ /// For high slip speeds the formula is replaced with an exponentially
600+ /// decaying function (with smooth coupling) which reaches 40% of
601+ /// maximum adhesion at infinity. The transition point between equations
602+ /// is at dV = sqrt(3)*K/umax (inflection point)
603+ ///
627604 /// </summary>
628605 /// <param name="slipSpeedMpS">Difference between train speed and wheel speed</param>
629606 /// <param name="speedMpS">Current speed</param>
@@ -637,7 +614,7 @@ public float SlipCharacteristics(float slipSpeedMpS, float speedMpS, float K, fl
637614 float absx = Math . Abs ( x ) ;
638615 float sqrt3 = ( float ) Math . Sqrt ( 3 ) ;
639616 if ( absx > sqrt3 )
640- {
617+ {
641618 // At infinity, adhesion is 40% of maximum (Polach, 2005)
642619 // The value must be lower than 85% for the formula to work
643620 float inftyFactor = 0.4f ;
0 commit comments