@@ -572,7 +572,10 @@ public Direction Direction
572572 public float TotalWagonLateralDerailForceN ;
573573 public float LateralWindForceN ;
574574 public float WagonFrontCouplerAngleRad ;
575+ public float WagonFrontCouplerBuffAngleRad ;
575576 public float WagonRearCouplerAngleRad ;
577+ public float WagonRearCouplerBuffAngleRad ;
578+ public float CarTrackPlayM = Me . FromIn ( 2.0f ) ;
576579 public float AdjustedWagonFrontCouplerAngleRad ;
577580 public float AdjustedWagonRearCouplerAngleRad ;
578581 public float WagonFrontCouplerCurveExtM ;
@@ -1196,6 +1199,10 @@ public virtual void UpdateTunnelForce()
11961199 ///
11971200 /// Hence these calculations provide a "generic" approach to determining whether a car will derial or not.
11981201 ///
1202+ /// Buff Coupler angle calculated from this publication: In-Train Force Limit Study by National Research Council Canada
1203+ ///
1204+ /// https://nrc-publications.canada.ca/eng/view/ft/?id=8cc206d0-5dbd-42ed-9b4e-35fd9f8b8efb
1205+ ///
11991206 /// </summary>
12001207
12011208 public void UpdateTrainDerailmentRisk ( float elapsedClockSeconds )
@@ -1336,6 +1343,69 @@ public void UpdateTrainDerailmentRisk(float elapsedClockSeconds)
13361343 AdjustedWagonRearCouplerAngleRad = WagonRearCouplerAngleRad ;
13371344 CarBehind . AdjustedWagonFrontCouplerAngleRad = CarBehind . WagonFrontCouplerAngleRad ;
13381345 }
1346+
1347+ // Only process this code segment if coupler is in compression
1348+ if ( CouplerForceU > 0 && CouplerSlackM < 0 )
1349+ {
1350+
1351+ // Calculate Buff coupler angles. Car1 is current car, and Car2 is the car behind
1352+ // Car ahead rear coupler angle
1353+ var ThiscarCouplerlengthft = Me . ToFt ( CarCouplerFaceLengthM - CarBodyLengthM ) + CouplerSlackM / 2 ;
1354+ var CarbehindCouplerlengthft = Me . ToFt ( CarBehind . CarCouplerFaceLengthM - CarBehind . CarBodyLengthM ) + CouplerSlackM / 2 ;
1355+ var A1 = Math . Sqrt ( Math . Pow ( Me . ToFt ( CurrentCurveRadius ) , 2 ) - Math . Pow ( Me . ToFt ( CarBogieCentreLengthM ) , 2 ) / 4.0f ) ;
1356+ var A2 = ( Me . ToFt ( CarCouplerFaceLengthM ) / 2.0f ) - ThiscarCouplerlengthft ;
1357+ var A = ( float ) Math . Atan ( A1 / A2 ) ;
1358+
1359+ var B = ( float ) Math . Asin ( 2.0f * Me . ToFt ( CarTrackPlayM ) / Me . ToFt ( CarBogieCentreLengthM ) ) ;
1360+ var C1 = Math . Pow ( ThiscarCouplerlengthft + CarbehindCouplerlengthft , 2 ) ;
1361+
1362+ var C2_1 = Math . Sqrt ( Math . Pow ( Me . ToFt ( CarCouplerFaceLengthM ) / 2.0f - ThiscarCouplerlengthft , 2 ) + Math . Pow ( Me . ToFt ( CurrentCurveRadius ) , 2 ) - Math . Pow ( Me . ToFt ( CarBogieCentreLengthM ) , 2 ) / 4.0f ) ;
1363+ var C2_2 = ( 2.0f * Me . ToFt ( CarTrackPlayM ) * ( Me . ToFt ( CarCouplerFaceLengthM ) / 2.0f - ThiscarCouplerlengthft ) ) / Me . ToFt ( CarBogieCentreLengthM ) ;
1364+ var C2 = Math . Pow ( ( C2_1 + C2_2 ) , 2 ) ;
1365+
1366+ var C3_1 = Math . Sqrt ( Math . Pow ( Me . ToFt ( CarBehind . CarCouplerFaceLengthM ) / 2.0f - CarbehindCouplerlengthft , 2 ) + Math . Pow ( Me . ToFt ( CurrentCurveRadius ) , 2 ) - Math . Pow ( Me . ToFt ( CarBehind . CarBogieCentreLengthM ) , 2 ) / 4.0f ) ;
1367+ var C3_2 = ( 2.0f * Me . ToFt ( CarBehind . CarTrackPlayM ) * ( Me . ToFt ( CarBehind . CarCouplerFaceLengthM ) / 2.0f - CarbehindCouplerlengthft ) ) / Me . ToFt ( CarBehind . CarBogieCentreLengthM ) ;
1368+ var C3 = Math . Pow ( ( C3_1 + C3_2 ) , 2 ) ;
1369+
1370+ var C4 = 2.0f * ( ThiscarCouplerlengthft + CarbehindCouplerlengthft ) * ( C2_1 + C2_2 ) ;
1371+
1372+ var C = ( float ) Math . Acos ( ( C1 + C2 - C3 ) / C4 ) ;
1373+
1374+ WagonRearCouplerBuffAngleRad = MathHelper . ToRadians ( 180.0f ) - A + B - C ;
1375+
1376+
1377+ // Trace.TraceInformation("Buff - CarId {0} Carahead {1} A {2} B {3} C {4} 180 {5}", CarID, CarAhead.WagonRearCouplerBuffAngleRad, A, B, C, MathHelper.ToRadians(180.0f));
1378+
1379+
1380+
1381+ // This car front coupler angle
1382+ var X1 = Math . Sqrt ( Math . Pow ( Me . ToFt ( CurrentCurveRadius ) , 2 ) - Math . Pow ( Me . ToFt ( CarBehind . CarBogieCentreLengthM ) , 2 ) / 4.0f ) ;
1383+ var X2 = ( Me . ToFt ( CarBehind . CarCouplerFaceLengthM ) / 2.0f ) - CarbehindCouplerlengthft ;
1384+ var X = ( float ) Math . Atan ( X1 / X2 ) ;
1385+
1386+ var Y = ( float ) Math . Asin ( 2.0f * Me . ToFt ( CarBehind . CarTrackPlayM ) / Me . ToFt ( CarBehind . CarBogieCentreLengthM ) ) ;
1387+
1388+ var Z1 = Math . Pow ( ThiscarCouplerlengthft + CarbehindCouplerlengthft , 2 ) ;
1389+ var Z2_1 = Math . Sqrt ( Math . Pow ( Me . ToFt ( CarBehind . CarCouplerFaceLengthM ) / 2.0f - CarbehindCouplerlengthft , 2 ) + Math . Pow ( Me . ToFt ( CurrentCurveRadius ) , 2 ) - Math . Pow ( Me . ToFt ( CarBehind . CarBogieCentreLengthM ) , 2 ) / 4.0f ) ;
1390+ var Z2_2 = ( 2.0f * Me . ToFt ( CarBehind . CarTrackPlayM ) * ( Me . ToFt ( CarBehind . CarCouplerFaceLengthM ) / 2.0f - CarbehindCouplerlengthft ) ) / Me . ToFt ( CarBehind . CarBogieCentreLengthM ) ;
1391+ var Z2 = Math . Pow ( ( Z2_1 + Z2_2 ) , 2 ) ;
1392+
1393+ var Z3_1 = Math . Sqrt ( Math . Pow ( Me . ToFt ( CarCouplerFaceLengthM ) / 2.0f - ThiscarCouplerlengthft , 2 ) + Math . Pow ( Me . ToFt ( CurrentCurveRadius ) , 2 ) - Math . Pow ( Me . ToFt ( CarBogieCentreLengthM ) , 2 ) / 4.0f ) ;
1394+ var Z3_2 = ( 2.0f * Me . ToFt ( CarTrackPlayM ) * ( Me . ToFt ( CarCouplerFaceLengthM ) / 2.0f - ThiscarCouplerlengthft ) ) / Me . ToFt ( CarBogieCentreLengthM ) ;
1395+ var Z3 = Math . Pow ( ( Z3_1 + Z3_2 ) , 2 ) ;
1396+
1397+ var Z4 = 2.0f * ( ThiscarCouplerlengthft + CarbehindCouplerlengthft ) * ( Z2_1 + Z2_2 ) ;
1398+
1399+ var Z = ( float ) Math . Acos ( ( Z1 + Z2 - Z3 ) / Z4 ) ;
1400+
1401+ CarBehind . WagonFrontCouplerBuffAngleRad = MathHelper . ToRadians ( 180.0f ) - X + Y - Z ;
1402+
1403+ // Trace.TraceInformation("Buff - CarId {0} Thiscar {1} A {2} B {3} C {4} 180 {5}", CarID, WagonFrontCouplerBuffAngleRad, X, Y, Z, MathHelper.ToRadians(180.0f));
1404+
1405+ // Trace.TraceInformation("Buff - CarId {0} StringThis {1} StringBehind {2} BuffThis {3} BuffAhead {4}", CarID, WagonRearCouplerAngleRad, CarBehind.WagonFrontCouplerAngleRad, WagonRearCouplerBuffAngleRad, CarBehind.WagonFrontCouplerBuffAngleRad);
1406+
1407+ }
1408+
13391409 }
13401410 else if ( CarAhead != null )
13411411 {
@@ -1344,6 +1414,9 @@ public void UpdateTrainDerailmentRisk(float elapsedClockSeconds)
13441414 AdjustedWagonRearCouplerAngleRad = 0.0f ;
13451415 CarBehind . AdjustedWagonFrontCouplerAngleRad = 0.0f ;
13461416 WagonRearCouplerAngleRad = 0 ;
1417+ WagonFrontCouplerAngleRad = 0 ;
1418+ WagonRearCouplerBuffAngleRad = 0 ;
1419+ WagonFrontCouplerBuffAngleRad = 0 ;
13471420 CarBehind . WagonFrontCouplerAngleRad = 0 ;
13481421 CarAhead . WagonRearCouplerAngleRad = 0 ;
13491422 }
@@ -1424,7 +1497,16 @@ public void UpdateTrainDerailmentRisk(float elapsedClockSeconds)
14241497
14251498 if ( IsPlayerTrain )
14261499 {
1427- WagonCouplerAngleDerailRad = Math . Abs ( WagonRearCouplerAngleRad ) ;
1500+ if ( CouplerForceU > 0 && CouplerSlackM < 0 ) // If car coupler is in compression, use the buff angle
1501+ {
1502+ WagonCouplerAngleDerailRad = Math . Abs ( WagonRearCouplerBuffAngleRad ) ;
1503+ }
1504+ else // if coupler in tension, then use tension angle
1505+ {
1506+ WagonCouplerAngleDerailRad = Math . Abs ( WagonRearCouplerAngleRad ) ;
1507+ }
1508+
1509+
14281510 var numAxles = LocoNumDrvAxles + WagonNumAxles ;
14291511 var numWheels = numAxles * 2 ;
14301512
@@ -1504,7 +1586,8 @@ public void UpdateTrainDerailmentRisk(float elapsedClockSeconds)
15041586 }
15051587 else
15061588 {
1507- DerailmentCoefficient *= 1.4f ;
1589+ // DerailmentCoefficient *= 1.4f;
1590+ DerailmentCoefficient *= 2.0f ;
15081591 }
15091592
15101593 var wagonAdhesion = Train . WagonCoefficientFriction ;
0 commit comments