Skip to content

Commit a7f2303

Browse files
committed
Tweaks to default superelevation, adjustments to gravity and tilting to handle new system better
1 parent e4ba0d7 commit a7f2303

File tree

4 files changed

+60
-67
lines changed

4 files changed

+60
-67
lines changed

Source/Orts.Formats.Msts/RouteFile.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ public class SuperElevationStandard
240240
public float MaxPaxUnderbalanceM = float.PositiveInfinity; // Limit to be set elsewhere
241241
public float MinCantM = 0.010f; // Default 10 mm (0.5 inches on imperial routes)
242242
public float MaxCantM = 0.180f; // Default limit on superelevation is 180 mm (5 inches on imperial routes)
243-
public float MinSpeedMpS = MpS.FromKpH(25.0f); // Default 25 kmh (15 mph on imperial routes)
243+
public float MinSpeedMpS = MpS.FromKpH(25.5f); // Default 25 kmh (15 mph on imperial routes)
244244
public float MaxSpeedMpS = float.PositiveInfinity; // Default unlimited
245245
public float PrecisionM = 0.005f; // Default 5 mm (0.25 inches on imperial routes)
246246
public float RunoffSlope = 0.003f; // Maximum rate of change of superelevation per track length, default 0.3%
@@ -272,7 +272,7 @@ public SuperElevationStandard(bool metric = true, bool highSpeed = false)
272272
// Set values in imperial units
273273
MinCantM = Me.FromIn(0.5f);
274274
MaxCantM = Me.FromIn(6.0f);
275-
MinSpeedMpS = MpS.FromMpH(15.0f);
275+
MinSpeedMpS = MpS.FromMpH(15.5f);
276276
PrecisionM = Me.FromIn(0.25f);
277277

278278
if (highSpeed)

Source/Orts.Simulation/Simulation/Physics/Train.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4692,7 +4692,7 @@ public void CalculatePositionOfCars(float elapsedTime, float distance)
46924692

46934693
traveller.Move((car.CarLengthM - car.CarBogieCentreLengthM) / 2.0f); // Move to the front of the car
46944694

4695-
car.UpdatedTraveler(traveller, elapsedTime, distance, SpeedMpS);
4695+
car.UpdateVibration(traveller, elapsedTime, distance, SpeedMpS);
46964696
}
46974697
length += car.CarLengthM;
46984698
// update position of container in discrete freight animations
@@ -4785,7 +4785,7 @@ public void CalculatePositionOfEOT()
47854785

47864786
traveller.Move((car.CarLengthM - car.CarBogieCentreLengthM) / 2.0f); // Move to the front of the car
47874787

4788-
car.UpdatedTraveler(traveller, elapsedTime, distance, SpeedMpS);
4788+
car.UpdateVibration(traveller, elapsedTime, distance, SpeedMpS);
47894789
length += car.CarLengthM;
47904790
}
47914791
traveller = new Traveller(traveller, Traveller.TravellerDirection.Backward);
@@ -4821,7 +4821,7 @@ public void RecalculateRearTDBTraveller()
48214821
// advance to the front of the car
48224822
traveller.Move(car.CarLengthM);
48234823

4824-
car.UpdatedTraveler(traveller, elapsedTime, distance, SpeedMpS);
4824+
car.UpdateVibration(traveller, elapsedTime, distance, SpeedMpS);
48254825
length += car.CarLengthM;
48264826
}
48274827
RearTDBTraveller = new Traveller(traveller);

Source/Orts.Simulation/Simulation/RollingStocks/TrainCar.cs

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -972,12 +972,6 @@ public virtual void Update(float elapsedClockSeconds)
972972

973973
CarOutsideTempC = InitialCarOutsideTempC - TemperatureHeightVariationDegC;
974974

975-
// gravity force, M32 is up component of forward vector
976-
// Percent slope = rise / run -> the Y position of the forward vector gives us the 'rise'
977-
// Derive the 'run' by assuming a hypotenuse length of 1, so run = sqrt(1 - rise^2)
978-
float rise = WorldPosition.XNAMatrix.M32;
979-
GravityForceN = MassKG * GravitationalAccelerationMpS2 * rise;
980-
CurrentElevationPercent = 100f * (rise / (float)Math.Sqrt(1 - rise * rise));
981975
AbsSpeedMpS = Math.Abs(_SpeedMpS);
982976

983977
//TODO: next if block has been inserted to flip trainset physics in order to get viewing direction coincident with loco direction when using rear cab.
@@ -2172,7 +2166,7 @@ public virtual void Save(BinaryWriter outf)
21722166
outf.Write(CouplerSlackM);
21732167
outf.Write(Headlight);
21742168
outf.Write(OrgConsist);
2175-
outf.Write(PrevTiltingZRot);
2169+
outf.Write(TiltingZRot);
21762170
outf.Write(BrakesStuck);
21772171
outf.Write(IsCarHeatingInitialized);
21782172
outf.Write(SteamHoseLeakRateRandom);
@@ -2195,7 +2189,7 @@ public virtual void Restore(BinaryReader inf)
21952189
CouplerSlackM = inf.ReadSingle();
21962190
Headlight = inf.ReadInt32();
21972191
OrgConsist = inf.ReadString();
2198-
PrevTiltingZRot = inf.ReadSingle();
2192+
TiltingZRot = inf.ReadSingle();
21992193
BrakesStuck = inf.ReadBoolean();
22002194
IsCarHeatingInitialized = inf.ReadBoolean();
22012195
SteamHoseLeakRateRandom = inf.ReadSingle();
@@ -2758,6 +2752,8 @@ public void ComputePosition(Traveller traveler, bool backToFront, float elapsedT
27582752

27592753
UpdateCurvePhys(new Traveller(traveler), BogieZOffsets);
27602754

2755+
int direction = traveler.Direction == Traveller.TravellerDirection.Forward ? -1 : 1;
2756+
27612757
if (Flipped == backToFront)
27622758
{
27632759
float o = -CarLengthM / 2 - CentreOfGravityM.Z;
@@ -2773,13 +2769,12 @@ public void ComputePosition(Traveller traveler, bool backToFront, float elapsedT
27732769
location.X += 2048 * (traveler.TileX - tileX);
27742770
location.Z += 2048 * (traveler.TileZ - tileZ);
27752771

2776-
// This car is flipped, so flip roll direction.
2777-
r *= -1;
2778-
2779-
WheelAxles[k].Part.AddWheelSetLocation(1, o, location, r);
2772+
// This car is flipped, so flip roll direction in part
2773+
WheelAxles[k].Part.AddWheelSetLocation(1, o, location, -r);
27802774
}
27812775
o = CarLengthM / 2 - CentreOfGravityM.Z - o;
27822776
traveler.Move(o);
2777+
direction *= -1;
27832778
}
27842779
else
27852780
{
@@ -2823,7 +2818,7 @@ public void ComputePosition(Traveller traveler, bool backToFront, float elapsedT
28232818
p.FindCenterLine();
28242819
}
28252820
}
2826-
// Determine facing direction of train car
2821+
// Determine facing direction and position of train car
28272822
p0.FindCenterLine();
28282823
Vector3 fwd = new Vector3(p0.Dir[0], p0.Dir[1], -p0.Dir[2]);
28292824
// Check if null (0-length) vector
@@ -2839,16 +2834,23 @@ public void ComputePosition(Traveller traveler, bool backToFront, float elapsedT
28392834
m.Up = up;
28402835
m.Backward = fwd;
28412836

2837+
// Update gravity force when position is updated, but before any secondary motion is added
2838+
GravityForceN = MassKG * GravitationalAccelerationMpS2 * fwd.Y;
2839+
CurrentElevationPercent = 100f * (fwd.Y / (float)Math.Sqrt(1 - fwd.Y * fwd.Y));
2840+
2841+
// Consider body roll from superelevation and from tilting.
2842+
UpdateTilting(traveler, elapsedTimeS, speed, direction);
28422843
Matrix rollMat = Matrix.Identity;
2844+
float rollAngle = p0.Roll + TiltingZRot;
28432845

2844-
if (p0.Roll != 0.0f)
2846+
if (rollAngle != 0.0f)
28452847
{
28462848
// For correct bogie positioning, need to offset rotation axis
28472849
Vector3 offset = new Vector3(0.0f, BogiePivotHeightM, 0.0f);
28482850

28492851
// Roll the car for superelevation about the offset axis of rotation
28502852
rollMat.Translation -= offset;
2851-
rollMat *= Matrix.CreateRotationZ(p0.Roll);
2853+
rollMat *= Matrix.CreateRotationZ(rollAngle);
28522854
rollMat.Translation += offset;
28532855

28542856
m = rollMat * m;
@@ -2863,19 +2865,36 @@ public void ComputePosition(Traveller traveler, bool backToFront, float elapsedT
28632865
WorldPosition.TileX = tileX;
28642866
WorldPosition.TileZ = tileZ;
28652867

2866-
UpdatedTraveler(traveler, elapsedTimeS, distance, speed);
2868+
UpdateVibration(traveler, elapsedTimeS, distance, speed);
28672869
}
28682870

28692871
#region Traveller-based updates
28702872
public float CurrentCurveRadiusM;
28712873

2872-
internal void UpdatedTraveler(Traveller traveler, float elapsedTimeS, float distanceM, float speedMpS)
2874+
internal void UpdateTilting(Traveller traveller, float elapsedTimeS, float speedMpS, int direction)
28732875
{
2874-
// We need to avoid introducing any unbounded effects, so cap the elapsed time to 0.25 seconds (4FPS).
2875-
if (elapsedTimeS > 0.25f)
2876+
// If not a tilting train, skip processing this
2877+
// Future: Rework tilting to be per train car, instead of per consist
2878+
if (!Train.IsTilting)
28762879
return;
28772880

2878-
UpdateVibrationAndTilting(traveler, elapsedTimeS, distanceM, speedMpS);
2881+
float tiltDemand = 0.0f;
2882+
2883+
// No tilt needed if going too slow, or not on a curve
2884+
if (speedMpS > MinTiltSpeedMpS && CurrentCurveRadiusM != 0.0f)
2885+
{
2886+
// Compare actual superelevation to ideal, tilt makes up the difference
2887+
// Sine of superelevation angle is v^2 / (g * r)
2888+
float idealElevAngle = (speedMpS * speedMpS) / (GravitationalAccelerationMpS2 * CurrentCurveRadiusM);
2889+
idealElevAngle = (float)Math.Asin(Math.Min(idealElevAngle, 0.99f));
2890+
2891+
tiltDemand = MathHelper.Clamp(idealElevAngle - SuperElevationAngleRad, 0.0f, MaxTiltAngleRad);
2892+
2893+
tiltDemand *= direction * traveller.CurveDirection;
2894+
}
2895+
2896+
// Smooth rotation
2897+
TiltingZRot += (tiltDemand - TiltingZRot) * elapsedTimeS;
28792898
}
28802899
#endregion
28812900

@@ -2953,16 +2972,21 @@ public void UpdateCurvePhys(Traveller traveller, float[] offsets)
29532972
int VibrationTrackVectorSection;
29542973
float VibrationTrackCurvaturepM;
29552974

2956-
float PrevTiltingZRot; // previous tilting angle
2957-
float TiltingZRot; // actual tilting angle
2975+
float TiltingZRot; // Actual tilting angle
2976+
float MinTiltSpeedMpS = MpS.FromKpH(50.0f); // Minimum speed for tilting to be activated
2977+
float MaxTiltAngleRad = MathHelper.ToRadians(8.0f); // Maximum angle of tilting allowed
29582978

2959-
internal void UpdateVibrationAndTilting(Traveller traveler, float elapsedTimeS, float distanceM, float speedMpS)
2979+
internal void UpdateVibration(Traveller traveler, float elapsedTimeS, float distanceM, float speedMpS)
29602980
{
2981+
// We need to avoid introducing any unbounded effects, so cap the elapsed time to 0.25 seconds (4FPS).
2982+
if (elapsedTimeS > 0.25f)
2983+
return;
29612984
// NOTE: Traveller is at the FRONT of the TrainCar!
29622985

29632986
// Don't add vibrations to train cars less than 2.5 meter in length; they're unsuitable for these calculations.
29642987
// Don't let vibrate car before EOT to avoid EOT not moving together with that car
2965-
if (CarLengthM < 2.5f || Train.EOT != null && Train.Cars.Count > 1 && Train.Cars[Train.Cars.Count - 2] == this) return;
2988+
if (CarLengthM < 2.5f || Train.EOT != null && Train.Cars.Count > 1 && Train.Cars[Train.Cars.Count - 2] == this)
2989+
return;
29662990
if (Simulator.Settings.CarVibratingLevel != 0)
29672991
{
29682992

@@ -3031,18 +3055,9 @@ internal void UpdateVibrationAndTilting(Traveller traveler, float elapsedTimeS,
30313055
AddVibrations(VibrationFactorTrackNode);
30323056
VibrationTrackNode = traveler.TrackNodeIndex;
30333057
}
3034-
}
3035-
if (Train != null && Train.IsTilting)
3036-
{
3037-
TiltingZRot = traveler.FindTiltedZ(speedMpS);//rotation if tilted, an indication of centrifugal force
3038-
TiltingZRot = PrevTiltingZRot + (TiltingZRot - PrevTiltingZRot) * elapsedTimeS;//smooth rotation
3039-
PrevTiltingZRot = TiltingZRot;
3040-
if (this.Flipped) TiltingZRot *= -1f;
3041-
}
3042-
if (Simulator.Settings.CarVibratingLevel != 0 || Train.IsTilting)
3043-
{
3044-
var rotation = Matrix.CreateFromYawPitchRoll(VibrationRotationRad.Y, VibrationRotationRad.X, VibrationRotationRad.Z + TiltingZRot);
3045-
var translation = Matrix.CreateTranslation(VibrationTranslationM.X, VibrationTranslationM.Y, 0);
3058+
3059+
Matrix rotation = Matrix.CreateFromYawPitchRoll(VibrationRotationRad.Y, VibrationRotationRad.X, VibrationRotationRad.Z);
3060+
Matrix translation = Matrix.CreateTranslation(VibrationTranslationM.X, VibrationTranslationM.Y, 0);
30463061
WorldPosition.XNAMatrix = rotation * translation * WorldPosition.XNAMatrix;
30473062
VibrationInverseMatrix = Matrix.Invert(rotation * translation);
30483063
}

Source/Orts.Simulation/Simulation/Traveller.cs

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ public TravellerDirection Direction
132132
/// </summary>
133133
public bool IsTrackCurved { get { return IsTrack && trackSection != null && trackSection.SectionCurve != null; } }
134134
/// <summary>
135+
/// Returns the direction of curvature based on track section angle. Returns 0 if not on a curve.
136+
/// </summary>
137+
public int CurveDirection { get { return (trackSection != null && trackSection.SectionCurve != null) ? Math.Sign(trackSection.SectionCurve.Angle) : 0; } }
138+
/// <summary>
135139
/// Returns whether this traveller is currently on a section of track which is straight.
136140
/// </summary>
137141
public bool IsTrackStraight { get { return IsTrack && (trackSection == null || trackSection.SectionCurve == null); } }
@@ -1113,32 +1117,6 @@ public Vector3 GetTrackDirection()
11131117
return directionVector;
11141118
}
11151119

1116-
public float FindTiltedZ(float speed) //will test 1 second ahead, computed will return desired elev. only
1117-
{
1118-
if (speed < 12) return 0;//no tilt if speed too low (<50km/h)
1119-
var tn = trackNode;
1120-
if (tn.TrVectorNode == null) return 0f;
1121-
var tvs = trackVectorSection;
1122-
var ts = trackSection;
1123-
var desiredZ = 0f;
1124-
if (tvs == null)
1125-
{
1126-
desiredZ = 0f;
1127-
}
1128-
else if (ts.SectionCurve != null)
1129-
{
1130-
float maxv = 0.14f * speed / 40f;//max 8 degree
1131-
//maxv *= speed / 40f;
1132-
//if (maxv.AlmostEqual(0f, 0.001f)) maxv = 0.02f; //short curve, add some effect anyway
1133-
var sign = -Math.Sign(ts.SectionCurve.Angle);
1134-
if ((this.direction == TravellerDirection.Forward ? 1 : -1) * sign > 0) desiredZ = 1f;
1135-
else desiredZ = -1f;
1136-
desiredZ *= maxv;//max elevation
1137-
}
1138-
else desiredZ = 0f;
1139-
return desiredZ;
1140-
}
1141-
11421120
/// <summary>
11431121
/// Finds the nearest junction node in the direction this traveller is facing.
11441122
/// </summary>

0 commit comments

Comments
 (0)