diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp
index 330e8eb8f..20761bacb 100644
--- a/McZapkie/Mover.cpp
+++ b/McZapkie/Mover.cpp
@@ -5147,6 +5147,7 @@ void TMoverParameters::ComputeTotalForce(double dt)
double TMoverParameters::BrakeForceR(double ratio, double velocity) const
{
double press = 0;
+ ratio = Hamulec->GetBrakeRatio(ratio);
if (MBPM > 2)
{
press = MaxBrakePress[1] + (MaxBrakePress[3] - MaxBrakePress[1]) * std::min(1.0, (TotalMass - Mass) / (MBPM - Mass));
@@ -5162,8 +5163,8 @@ double TMoverParameters::BrakeForceR(double ratio, double velocity) const
press = MaxBrakePress[3];
if (DynamicBrakeType == dbrake_automatic)
ratio = ratio + (1.5 - ratio) * std::min(1.0, Vel * 0.02);
- if (BrakeDelayFlag & bdelay_R && BrakeMethod % 128 != bp_Cosid && BrakeMethod % 128 != bp_D1 && BrakeMethod % 128 != bp_D2 && Power < 1 && velocity < 40)
- ratio = ratio / 2;
+ if (BrakeDelayFlag & bdelay_R && BrakeMethod % 128 != bp_Cosid && BrakeMethod % 128 != bp_D1 && BrakeMethod % 128 != bp_D2 && Power < 1)
+ ratio *= Hamulec->GetRapidRatio(velocity);
if (TrainType == dt_DMU && velocity < 30.0)
{
ratio -= 0.3;
diff --git a/McZapkie/Oerlikon_ESt.cpp b/McZapkie/Oerlikon_ESt.cpp
index 17fa79ee7..1e7a5be66 100644
--- a/McZapkie/Oerlikon_ESt.cpp
+++ b/McZapkie/Oerlikon_ESt.cpp
@@ -16,6 +16,11 @@ Copyright (C) 2007-2014 Maciej Cierniak
#include "Oerlikon_ESt.h"
#include "utilities/utilities.h"
+// wstepny nacisk sprezyn na glowny przyrzad rozrzadczy, wyrazony jako roznica cisnien ZS - PG [bar]
+static double const dPSprezyny = 0.21;
+// najmniejszy stopien hamowania w cylindrze [bar] (tablica 3-9, proby 12 i 22: 0,25-0,30)
+static double const pStopnia = 0.28;
+
double d2A( double const d )
{
return d * d * 0.7854 / 1000.0;
@@ -218,10 +223,11 @@ double TNESt3::GetPF( double const PP, double const dt, double const Vel ) // pr
CheckReleaser(dt);
// luzowanie
+ double const cel{ CelBCP( CVP, VVP ) };
if ((BrakeStatus & b_hld) == b_off)
dV =
PF(0.0, BCP, Nozzles[dTO] * nastG + (1.0 - nastG) * Nozzles[dOO])
- * dt * (0.1 + 4.9 * std::min(0.2, BCP - ((CVP - 0.05 - VVP) * BVM + 0.1)));
+ * dt * (0.1 + 4.9 * std::min(0.2, BCP - (cel > 0.0 ? cel + 0.1 : 0.0))); // bez stopnia luzuje do zera
else
dV = 0.0;
// BrakeCyl.Flow(-dV);
@@ -229,7 +235,7 @@ double TNESt3::GetPF( double const PP, double const dt, double const Vel ) // pr
if ( (BrakeStatus & b_on) == b_on && Przekladniki[1]->P() * HBG300 < MaxBP )
dV =
PF( BVP, BCP, Nozzles[dTN] * (nastG + 2.0 * (BCP < Podskok ? 1.0 : 0.0)) + Nozzles[dON] * (1.0 - nastG) )
- * dt * (0.1 + 4.9 * std::min( 0.2, (CVP - 0.05 - VVP) * BVM - BCP ) );
+ * dt * (0.1 + 4.9 * std::min( 0.2, cel - BCP ) );
else
dV = 0;
// BrakeCyl.Flow(-dV);
@@ -313,7 +319,7 @@ void TNESt3::Init( double const PP, double const HPP, double const LPP, double c
Miedzypoj->CreateCap(5.0);
Miedzypoj->CreatePress(PP);
- BVM = 1.0 / (HPP - 0.05 - LPP) * MaxBP;
+ BVM = 1.0 / (HPP - dPSprezyny - LPP) * MaxBP;
BrakeDelayFlag = BDF;
@@ -327,6 +333,17 @@ void TNESt3::Init( double const PP, double const HPP, double const LPP, double c
RapidStaly = false;
}
+double TNESt3::GetRapidRatio(double const Vel) const
+{
+ // ESt3 has no rapid relay, and with disc brakes the rapid step stays engaged at all speeds
+ if( ( PrzekRapid == nullptr ) || RapidStaly ) {
+ return 1.0;
+ }
+ // a braking vehicle comes from above, so the step is still engaged down to the speed
+ // at which GetPF() drops it, not the one at which it engages
+ return ( Vel < 50.0 ? 1.0 / PrzekRapid->GetRapidMult() : 1.0 );
+}
+
double TNESt3::GetCRP()
{
return CntrlRes->P();
@@ -334,6 +351,24 @@ double TNESt3::GetCRP()
// return Miedzypoj.P;
}
+double TNESt3::GetBrakeRatio( double const Ratio ) const // udzial cisnienia w cylindrze dla udzialu spadku w PG
+{
+ if( MaxBP <= 0.0 || BVM <= 0.0 ) {
+ return Ratio;
+ }
+ // pelny spadek sluzbowy w PG (HPP - LPP), odtworzony z przelozenia
+ double const spadek{ MaxBP / BVM + dPSprezyny };
+ double const cel{ ( Ratio * spadek - dPSprezyny ) * BVM };
+ return ( cel > 0.0 ? std::max( cel, pStopnia ) / MaxBP : 0.0 );
+}
+
+double TNESt3::CelBCP( double const CVP, double const VVP ) const // docelowe cisnienie w cylindrze
+{
+ double const cel{ ( CVP - dPSprezyny - VVP ) * BVM };
+ // gdy zawor hamuje, nie daje mniej niz najmniejszy stopien
+ return ( cel > 0.0 ? std::max( cel, pStopnia ) : cel );
+}
+
void TNESt3::CheckState(double const BCP, double & /*dV1*/) // glowny przyrzad rozrzadczy
{
double const VVP{ ValveRes->P() };
@@ -344,14 +379,14 @@ void TNESt3::CheckState(double const BCP, double & /*dV1*/) // glowny przyrzad r
Przys_blok = false;
// sprawdzanie stanu
- // if ((BrakeStatus and 1)=1)and(BCP>0.25)then
- if (VVP + 0.01 + BCP / BVM < CVP - 0.05 && Przys_blok)
+ double const cel{ CelBCP( CVP, VVP ) };
+ if (BCP < cel - 0.01 * BVM && Przys_blok)
BrakeStatus |= b_on | b_hld; // hamowanie stopniowe;
- else if (VVP - 0.01 + (BCP - 0.1) / BVM > CVP - 0.05)
+ else if (BCP - 0.1 > cel + 0.01 * BVM)
BrakeStatus &= ~( b_on | b_hld ); // luzowanie;
- else if (VVP + BCP / BVM > CVP - 0.05)
+ else if (BCP > cel)
BrakeStatus &= ~b_on; // zatrzymanie napelaniania;
- else if (VVP + (BCP - 0.1) / BVM < CVP - 0.05 && BCP > 0.25) // zatrzymanie luzowania
+ else if (BCP - 0.1 < cel && BCP > 0.25) // zatrzymanie luzowania
BrakeStatus |= b_hld;
if( ( BrakeStatus & b_hld ) == 0 )
@@ -380,7 +415,7 @@ void TNESt3::CheckReleaser(double const dt) // odluzniacz
// odluzniacz automatyczny
if ((BrakeStatus & b_rls) == b_rls)
{
- CntrlRes->Flow(PF(CVP, 0, 0.02) * dt);
+ CntrlRes->Flow(PF(CVP, 0, 0.0049) * dt); // ZS 15 l z 5,0 do 0,4 bar w 23 s (proba odbioru nr 27)
if (CVP < VVP + 0.3 || false == autom)
BrakeStatus &= ~b_rls;
}
@@ -549,7 +584,7 @@ void TNESt3::SetSize( int const size, std::string const ¶ms ) // ustawianie
{ // dON,dOO,dTN,dTO,dP,dS
Nozzles[dON] = 3.7;
Nozzles[dOO] = 2.50;
- Nozzles[dTN] = 1.65;
+ Nozzles[dTN] = 1.62;
Nozzles[dTO] = 1.39;
Nozzles[dP] = 2.65;
Nozzles[dPd] = 1.80;
@@ -638,6 +673,14 @@ void TNESt3::SetSize( int const size, std::string const ¶ms ) // ustawianie
Nozzles[dSd] = 1.1;
Nozzles[dSm] = 0.9;
+ if( PrzekCiagly != nullptr ) {
+ // dysze cylindra skalibrowane na komore impulsowa przekladnika AL2 (1 l) wg czasow z tablicy 3-9
+ Nozzles[dON] *= 0.914;
+ Nozzles[dTN] *= 0.885;
+ Nozzles[dOO] *= 0.82;
+ Nozzles[dTO] *= 0.8;
+ }
+
// przeliczanie z mm^2 na l/m
for (auto &nozzle : Nozzles)
{
@@ -646,7 +689,8 @@ void TNESt3::SetSize( int const size, std::string const ¶ms ) // ustawianie
for (int i = 1; i < 4; ++i)
{
Przekladniki[i]->BrakeRes = BrakeRes;
- Przekladniki[i]->CreateCap(i);
+ // komora impulsowa przekladnika AL2 ma 1 l
+ Przekladniki[i]->CreateCap( ( Przekladniki[i] == PrzekCiagly ) ? 1.0 : i );
Przekladniki[i]->CreatePress(BrakeCyl->P());
if (i < 3)
Przekladniki[i]->Next = Przekladniki[i + 1];
diff --git a/McZapkie/Oerlikon_ESt.h b/McZapkie/Oerlikon_ESt.h
index dca531568..02d65a285 100644
--- a/McZapkie/Oerlikon_ESt.h
+++ b/McZapkie/Oerlikon_ESt.h
@@ -111,6 +111,7 @@ class TRapid : public TPrzekladnik {
public:
void SetRapidParams(double mult, double size);
void SetRapidStatus(bool rs);
+ double GetRapidMult() const { return RapidMult; }
void Update(double dt) override;
using TPrzekladnik::TPrzekladnik;
};
@@ -194,6 +195,9 @@ class TNESt3 : public TBrake {
double GetPF( double const PP, double const dt, double const Vel ) override; // przeplyw miedzy komora wstepna i PG
void EStParams(double i_crc) const; // parametry charakterystyczne dla ESt
double GetCRP() override;
+ double GetRapidRatio(double const Vel) const override;
+ double CelBCP(double const CVP, double const VVP) const; // docelowe cisnienie w cylindrze
+ double GetBrakeRatio(double const Ratio) const override; // udzial cisnienia w cylindrze dla udzialu spadku w PG
void CheckState(double const BCP, double &dV1); // glowny przyrzad rozrzadczy
void CheckReleaser(double const dt); // odluzniacz
double CVs(double const BP) const; // napelniacz sterujacego
diff --git a/McZapkie/hamulce.h b/McZapkie/hamulce.h
index 3074a2e7e..37b9b6a4d 100644
--- a/McZapkie/hamulce.h
+++ b/McZapkie/hamulce.h
@@ -421,6 +421,10 @@ class TBrake
double GetVRP() const; // cisnienie komory wstepnej rozdzielacza
/// Returns control reservoir (ZS) pressure [bar]; defaults to the auxiliary reservoir for valves without a dedicated ZS.
virtual double GetCRP(); // cisnienie zbiornika sterujacego
+ /// Share of the full cylinder pressure the distributor settles at for the given share
+ /// of the full service pipe drop. Used only to predict the brake force; the default is proportional.
+ /// Share of the full service brake pipe drop.
+ virtual double GetBrakeRatio(double const Ratio) const { return Ratio; }
///
/// Sets the brake delay (G/P/R/M) selector if requested mode is supported.
///
@@ -443,6 +447,11 @@ class TBrake
/// Sets the rapid step ratio. Default no-op; overridden where supported.
/// Rapid ratio.
virtual void SetRM(double const RMR) { /* no effect for this device type */ } // ustalenie przelozenia rapida
+ /// Share of the full cylinder pressure the distributor delivers at the given speed
+ /// while the vehicle is slowing down through it, for a vehicle set to R. Used only to predict
+ /// the brake force; the default is the approximation BrakeForceR() has always used.
+ /// Vehicle velocity [km/h].
+ virtual double GetRapidRatio(double const Vel) const { return Vel < 40.0 ? 0.5 : 1.0; }
/// Sets the velocity threshold for the rapid step.
/// Velocity threshold (same unit as Vel passed to GetPF).
virtual void SetRV(double const RVR)
diff --git a/tests/unit/scenario.cpp b/tests/unit/scenario.cpp
index b933a0ab5..4af8be18a 100644
--- a/tests/unit/scenario.cpp
+++ b/tests/unit/scenario.cpp
@@ -5,6 +5,21 @@
namespace test {
+namespace {
+
+// whether the pressure switch of a vehicle in the train still cuts the start off: a half of a locomotive whose
+// switch is on when the controller leaves zero doesn't close its line contactors until the controller is back at zero
+bool start_cut_off(consist &Train) {
+ for (std::size_t i = 0; i < Train.size(); ++i) {
+ if (Train[i].mover().ControlPressureSwitch) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
scenario::scenario(std::string Name, std::vector Channels, track const &Track) : m_name(std::move(Name)), m_channels(std::move(Channels)), m_track(Track) {
reseed_random();
}
@@ -53,6 +68,11 @@ void scenario::get_going() {
void scenario::full_power(double Seconds) {
auto &controlling{driver().controlling};
+ // as a driver does, wait for the brakes to let go far enough for every pressure switch of the train to allow the
+ // start; otherwise whether all the halves of a locomotive start hangs on thousandths of a bar of their cylinders
+ for (int i = 0; i < 120 && start_cut_off(m_train); ++i) {
+ wait(1.0);
+ }
controlling.IncMainCtrl(1);
for (int i = 0; i < 90 && false == controlling.StLinFlag; ++i) {
wait(1.0);
diff --git a/vehicle/Driver.cpp b/vehicle/Driver.cpp
index 08f0c0d24..9f83b6d53 100644
--- a/vehicle/Driver.cpp
+++ b/vehicle/Driver.cpp
@@ -2931,6 +2931,9 @@ bool TController::ReleaseEngine() {
return OK;
}
+// defined further down, with the braking demand it is used for
+static double brake_response_horizon( TMoverParameters const *Vehicle, TBrakeSystem const Brakesystem, double const Acceleration, double const Brakea0 );
+
bool TController::IncBrake()
{ // zwiększenie hamowania
auto OK { false };
@@ -3043,7 +3046,7 @@ bool TController::IncBrake()
}
double deltaAcc = -AccDesired*BrakeAccFactor() - (fBrake_a0[0] + 4.0 * (/*GBH mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition - 1 - pos_corr)*fBrake_a1[0]);
- if( deltaAcc > fBrake_a1[0])
+ if( deltaAcc > brake_increase_margin() )
{
if( /*GBH mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition < 0.1 ) {
OK = /*mvOccupied->*/BrakeLevelAdd( BrakingInitialLevel ); //GBH
@@ -3070,6 +3073,20 @@ bool TController::IncBrake()
}
}
}
+ else if( BrakeCtrlPosition > 0.0
+ && AccDesired < 0.0 // only while braking is asked for, not when called for a train slipping back while starting
+ && ( fBrakeHeldTime > (
+ mvOccupied->BrakeDelayFlag > bdelay_G ?
+ mvOccupied->BrakeDelay[ 1 ] :
+ mvOccupied->BrakeDelay[ 3 ] )
+ // a train falling further behind needs no full fill time to be seen not to get enough
+ || ( ( fShortfall > 0.02 )
+ && ( fBrakeHeldTime > brake_response_horizon( mvOccupied, BrakeSystem, 1.0, fBrake_a0[ 0 ] ) ) ) ) ) {
+ // the table says the present position is enough, but the brake has had its full fill
+ // time since the handle last moved and the train is still not slowing down as asked:
+ // believe the train rather than the table
+ OK = BrakeLevelAdd( BrakingLevelIncrease );
+ }
else
OK = false;
}
@@ -3186,7 +3203,9 @@ bool TController::DecBrake() {
if( fBrake_a0[0] != 0.0
|| fBrake_a1[0] != 0.0 ) {
auto const pos_diff { ( is_dmu() ? 0.25 : 1.0 ) };
- deltaAcc = -AccDesired * BrakeAccFactor() - ( fBrake_a0[ 0 ] + 4 * (/*GBH mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition - pos_diff )*fBrake_a1[ 0 ] );
+ // a distributor applied at all gives at least its minimum stage, about what the first position does, so
+ // a position short of the first one brakes no weaker than that
+ deltaAcc = -AccDesired * BrakeAccFactor() - ( fBrake_a0[ 0 ] + 4 * std::max( 0.0, BrakeCtrlPosition - pos_diff ) * fBrake_a1[ 0 ] );
}
if (deltaAcc < 0 && /*GBH mvOccupied->BrakeCtrlPosR*/ BrakeCtrlPosition > 0) {
OK = /*mvOccupied->*/ BrakeLevelAdd(-0.25);
@@ -3716,6 +3735,7 @@ void TController::BrakeLevelSet(double b)
if (BrakeCtrlPosition == b)
return; // nie przeliczać, jak nie ma zmiany
BrakeCtrlPosition = std::clamp(b, (double)gbh_MIN, (double)gbh_MAX);
+ fBrakeHeldTime = 0.0;
}
bool TController::BrakeLevelAdd(double b)
@@ -5853,6 +5873,7 @@ TController::update_timers( double dt ) {
ElapsedTime += dt;
WaitingTime += dt;
fBrakeTime -= dt; // wpisana wartość jest zmniejszana do 0, gdy ujemna należy zmienić nastawę hamulca
+ fBrakeHeldTime += dt;
if( mvOccupied->fBrakeCtrlPos != mvOccupied->Handle->GetPos( bh_FS ) ) {
// brake charging timeout starts after charging ends
BrakeChargingCooldown += dt;
@@ -6656,12 +6677,27 @@ TController::determine_proximity_ranges() {
// na jaka odleglosc i z jaka predkoscia ma podjechac do przeszkody
// jeśli pociąg
if( is_train() ) {
- fMinProximityDist = std::clamp( 5 + iVehicles, 10, 15 );
- fMaxProximityDist = std::clamp( 10 + iVehicles, 15, 40 );
+ if( IsAtPassengerStop ) {
+ // a stop point keeps the margins its W4 parameters (head or middle of the consist,
+ // platform length) were set up against
+ fMinProximityDist = std::clamp( 5 + iVehicles, 10, 15 );
+ fMaxProximityDist = std::clamp( 10 + iVehicles, 15, 40 );
+ if( IsCargoTrain ) {
+ fMinProximityDist += 10.0;
+ }
+ }
+ else {
+ // a train stops 8-15 m short of the signal whatever its length; closer than the lower
+ // edge it stops at once, the braking aims at the middle of the window, 3.5 m further out
+ fMinProximityDist = 8.0;
+ // the final approach begins far enough out to slow from the coasting speed down to the window,
+ // and at the same distance whether the train is moving or standing, so it does not flip
+ // between creeping closer and holding still when it comes to a stand short of the window
+ fMaxProximityDist = 50.0;
+ }
if( IsCargoTrain ) {
- // increase distances for cargo trains to take into account slower reaction to brakes
- fMinProximityDist += 10.0;
+ // start the final approach earlier for cargo trains, their brakes react more slowly
fMaxProximityDist += 10.0;
/*
if( IsHeavyCargoTrain ) {
@@ -6680,7 +6716,7 @@ TController::determine_proximity_ranges() {
if( mvOccupied->Vel < 0.1 ) {
// jak stanie za daleko, to niech nie dociąga paru metrów
- fMaxProximityDist = 50.0;
+ fMaxProximityDist = std::max( fMaxProximityDist, 50.0 );
}
if( iDrivigFlags & moveLate ) {
@@ -7293,6 +7329,26 @@ TController::pick_optimal_speed( double const Range ) {
is_car() ? -2.0 : -0.9,
is_car() ? 2.0 : 0.9 );
+ if( is_train()
+ && ( ( OrderCurrentGet() & Obey_train ) != 0 )
+ && ( VelNext == 0.0 )
+ && ( ActualProximityDist <= fMaxProximityDist )
+ && ( fAccGravity < -0.025 ) ) {
+ // uphill, the gradient alone brings a slow train to a stand within metres, far sooner than a released
+ // brake applies; so the train brake goes on while the train is coming to a stand, stays on while it
+ // stands, and is applied for real if the train has started to roll back. done last, so that none of
+ // the adjustments above, made for a train moving forward, can undo it
+ auto const vel { DirectionalVel() };
+ if( std::abs( vel ) < EU07_AI_NOMOVEMENT ) {
+ VelDesired = 0.0;
+ AccDesired = std::min( AccDesired, EU07_AI_NOACCELERATION );
+ }
+ else if( ( vel < 0.0 )
+ || ( ( vel < 5.0 ) && ( AccDesired < 0.0 ) ) ) {
+ AccDesired = std::min( AccDesired, fAccGravity - 0.15 );
+ }
+ }
+
// if the route ahead is blocked we might need to head the other way
check_route_behind( 1000 ); // NOTE: legacy scan range value
}
@@ -7458,6 +7514,74 @@ TController::adjust_desired_speed_for_limits() {
}
}
+// how long the present acceleration of a consist lasts whatever is done with the handle now: half the time
+// its pneumatic brake takes to apply (while speeding up) or to release (while slowing down), which in G
+// is tens of seconds; never shorter than the look-ahead the AI has always used
+static double
+brake_response_horizon( TMoverParameters const *Vehicle, TBrakeSystem const Brakesystem, double const Acceleration, double const Brakea0 ) {
+
+ auto horizon { ( 1.0 - Brakea0 ) * 30.0 / 3.6 };
+ if( Brakesystem == TBrakeSystem::Pneumatic ) {
+ auto const isrelease { Acceleration < 0.0 };
+ horizon = std::max(
+ horizon,
+ 0.5 * (
+ Vehicle->BrakeDelayFlag > bdelay_G ?
+ Vehicle->BrakeDelay[ isrelease ? 0 : 1 ] :
+ Vehicle->BrakeDelay[ isrelease ? 2 : 3 ] ) );
+ }
+ return horizon;
+}
+
+// distance [m] in which the gradient alone, with no brake and no power, brings a train rolling uphill at Velocity [km/h] to a stand
+static double
+uphill_stopping_distance( double const Velocity, double const Gravity ) {
+ return ( Velocity / 3.6 ) * ( Velocity / 3.6 ) / ( 2.0 * std::max( 0.01, -Gravity ) );
+}
+
+// speed [km/h] from which the gradient alone brings a train rolling uphill to a stand in Distance [m]
+static double
+uphill_coasting_speed( double const Distance, double const Gravity ) {
+ return 3.6 * std::sqrt( 2.0 * std::max( 0.01, -Gravity ) * std::max( 0.0, Distance ) );
+}
+
+// resultant acceleration [m/s2] to ask for now so that a steady application of the pneumatic brake takes the train
+// from Velocity down to Target [km/h] over Distance [m], with the brake force following the speed the way the
+// consist brake table says it does (cast iron blocks grip harder as the train slows)
+static double
+shaped_braking_demand(
+ double const Velocity, double const Target, double const Distance, double const Gravity,
+ double const Vmax, double const *Brakea0, double const *Brakea1, int const Tablesize ) {
+
+ auto const fullbraking = [&]( double const Kmh ) {
+ auto const index { std::clamp( static_cast( Tablesize * Kmh / std::max( 1.0, Vmax ) ), 1, Tablesize ) };
+ return std::max( 0.01, Brakea0[ index ] + 12.0 * Brakea1[ index ] ); };
+ auto const travel = [&]( double const Ratio ) {
+ auto const steps { 24 };
+ auto const step { ( Velocity - Target ) / steps };
+ auto distance { 0.0 };
+ for( auto i { 0 }; i < steps; ++i ) {
+ auto const kmh { Target + ( i + 0.5 ) * step };
+ auto const deceleration { Ratio * fullbraking( kmh ) - Gravity };
+ if( deceleration <= 0.001 ) {
+ return std::numeric_limits::max();
+ }
+ distance += ( kmh / 3.6 ) * ( step / 3.6 ) / deceleration;
+ }
+ return distance; };
+
+ auto low { 0.0 };
+ auto high { 3.0 };
+ if( travel( high ) > Distance ) {
+ return Gravity - high * fullbraking( Velocity );
+ }
+ for( auto i { 0 }; i < 30; ++i ) {
+ auto const middle { 0.5 * ( low + high ) };
+ ( travel( middle ) > Distance ? low : high ) = middle;
+ }
+ return Gravity - high * fullbraking( Velocity );
+}
+
void
TController::adjust_desired_speed_for_target_speed( double const Range ) {
// ustalanie zadanego przyspieszenia
@@ -7471,6 +7595,7 @@ TController::adjust_desired_speed_for_target_speed( double const Range ) {
// gdy jedzie wolniej niż potrzeba, albo nie ma przeszkód na drodze
// normalna jazda
AccDesired = VelDesired != 0.0 ? AccPreferred : -0.01;
+ fAccDesiredGravity = 0.0;
auto const vel { DirectionalVel() };
@@ -7490,7 +7615,10 @@ TController::adjust_desired_speed_for_target_speed( double const Range ) {
}
else if (ActualProximityDist > fMinProximityDist) {
// jedzie szybciej, niż trzeba na końcu ActualProximityDist, ale jeszcze jest daleko
- if (ActualProximityDist < fMaxProximityDist) {
+ // only a stop has a final approach of its own inside the proximity range; a lower speed ahead
+ // keeps the braking curve below right up to where it begins, or the train would stop braking
+ // (and even take power) that far out
+ if( ( VelNext == 0.0 ) && ( ActualProximityDist < fMaxProximityDist ) ) {
// jak minął już maksymalny dystans po prostu hamuj (niski stopień)
// ma stanąć, a jest w drodze hamowania albo ma jechać
/*
@@ -7507,9 +7635,33 @@ TController::adjust_desired_speed_for_target_speed( double const Range ) {
}
else {
// hamowanie tak, aby stanąć
- VelDesired = VelNext;
- AccDesired = ( VelNext * VelNext - vel * vel ) / ( 25.92 * ( ActualProximityDist + 0.1 - 0.5*fMinProximityDist ) );
- AccDesired = std::min( AccDesired, fAccThreshold );
+ if( ( OrderCurrentGet() & Obey_train ) == 0 ) {
+ VelDesired = VelNext;
+ AccDesired = ( VelNext * VelNext - vel * vel ) / ( 25.92 * ( ActualProximityDist + 0.1 - 0.5*fMinProximityDist ) );
+ AccDesired = std::min( AccDesired, fAccThreshold );
+ }
+ else {
+ // a train keeps its desired speed until the lower edge of the stopping window (a desired
+ // speed of 0 here asks for hard braking whatever the distance left), aims at the middle
+ // of the window, and asks only for what that takes
+ auto const stoppingroom { std::max( 0.5, ActualProximityDist - stopping_margin() ) };
+ AccDesired = (
+ BrakeSystem == TBrakeSystem::Pneumatic ?
+ // cast iron blocks grip harder as the train comes to a stand
+ shaped_braking_demand(
+ vel, VelNext, stoppingroom, fAccGravity,
+ mvOccupied->Vmax, fBrake_a0, fBrake_a1, BrakeAccTableSize ) :
+ ( VelNext * VelNext - vel * vel ) / ( 25.92 * stoppingroom ) );
+ if( ( fAccGravity < -0.025 )
+ && ( AccDesired >= fAccGravity - 0.001 ) // no brake needed at all
+ && ( ActualProximityDist - uphill_stopping_distance( vel, fAccGravity ) > stopping_margin() ) ) {
+ // uphill the gradient alone would bring the train to a stand short of the
+ // middle of the stopping window; keep it rolling at the present speed until that changes,
+ // but no faster than the gradient alone can take off by the middle of the window
+ AccDesired = std::min( 0.0, AccPreferred );
+ VelDesired = min_speed( VelDesired, uphill_coasting_speed( ActualProximityDist - stopping_margin(), fAccGravity ) );
+ }
+ }
}
}
else {
@@ -7533,17 +7685,57 @@ TController::adjust_desired_speed_for_target_speed( double const Range ) {
if( brakingdistance + std::max(slowdowndistance, fMaxProximityDist) >= ActualProximityDist - fMaxProximityDist ) {
// don't slow down prematurely; as long as we have room to come to a full stop at a safe distance, we're good
// ensure some minimal coasting speed, otherwise a vehicle entering this zone at very low speed will be crawling forever
- auto const brakingpointoffset = VelNext * braking_distance_multiplier( VelNext );
- AccDesired = std::min(
- AccDesired,
- ( VelNext * VelNext - vel * vel )
- / ( 25.92
- * std::max(
- ActualProximityDist - brakingpointoffset,
- std::min(
- ActualProximityDist,
- brakingpointoffset ) )
- + 0.1 ) ); // najpierw hamuje mocniej, potem zluzuje
+ auto const brakingpointoffset = (
+ ( VelNext == 0.0 ) && is_train() && ( OrderCurrentGet() & Obey_train ) ?
+ stopping_margin() : // the same stopping point as the final approach
+ VelNext * braking_distance_multiplier( VelNext ) );
+ // a pneumatic brake acts on the state the train will be in once it has responded; while the
+ // train is still speeding up, plan the deceleration from there, so more is asked for at once
+ auto velocity { vel };
+ auto distance { ActualProximityDist };
+ if( ( BrakeSystem == TBrakeSystem::Pneumatic )
+ && ( AbsAccS > 0.0 ) ) {
+ auto const horizon { brake_response_horizon( mvOccupied, BrakeSystem, AbsAccS, fBrake_a0[ 0 ] ) };
+ velocity = vel + 3.6 * horizon * AbsAccS;
+ distance = std::max( 0.5 * ActualProximityDist, ActualProximityDist - ( vel + velocity ) * 0.5 / 3.6 * horizon );
+ }
+ auto const brakingroom {
+ std::max(
+ distance - brakingpointoffset,
+ std::min(
+ distance,
+ brakingpointoffset ) ) };
+ auto deceleration { ( VelNext * VelNext - velocity * velocity ) / ( 25.92 * brakingroom + 0.1 ) };
+ if( ( BrakeSystem == TBrakeSystem::Pneumatic ) && ( velocity > VelNext ) ) {
+ // follow the shape of the consist brake force over speed rather than assume a constant
+ // deceleration, over the distance left once the brake has had time to fill (while the
+ // train is still speeding up, the prediction above has taken that off already)
+ auto room { brakingroom };
+ if( ( AbsAccS <= 0.0 )
+ && ( BrakeCtrlPosition <= 0.0 ) ) { // only while the brake is still to be applied: once it acts, the room it needs to respond is behind
+ room = std::max(
+ 0.5 * brakingroom,
+ brakingroom - vel / 3.6 * brake_response_horizon( mvOccupied, BrakeSystem, 1.0, fBrake_a0[ 0 ] ) );
+ }
+ deceleration = shaped_braking_demand(
+ velocity, VelNext, room + 0.1 / 25.92, fAccGravity,
+ mvOccupied->Vmax, fBrake_a0, fBrake_a1, BrakeAccTableSize );
+ }
+ AccDesired = std::min( AccDesired, deceleration ); // najpierw hamuje mocniej, potem zluzuje
+ if( vel > VelNext ) {
+ if( deceleration < fAccGravity - 0.001 ) {
+ // no power while still above the speed being braked for
+ AccDesired = std::min( AccDesired, EU07_AI_NOACCELERATION );
+ }
+ else if( ( fAccGravity < -0.025 )
+ && ( ( OrderCurrentGet() & Obey_train ) != 0 )
+ && ( ActualProximityDist - uphill_stopping_distance( vel, fAccGravity ) > brakingpointoffset ) ) {
+ // uphill the gradient alone slows the train down more than it is asked to;
+ // keep it rolling at the present speed until that changes
+ AccDesired = std::min( 0.0, AccPreferred );
+ VelDesired = min_speed( VelDesired, uphill_coasting_speed( ActualProximityDist - brakingpointoffset, fAccGravity ) );
+ }
+ }
}
}
}
@@ -7629,35 +7821,61 @@ TController::adjust_desired_speed_for_current_speed() {
if( fAccGravity > 0.025 ) {
// going sharply downhill we may need to start braking sooner than usual
// try to estimate increase of current velocity before engaged brakes start working
- auto const speedestimate = vel + ( 1.0 - fBrake_a0[ 0 ] ) * 30.0 * AbsAccS;
- if( speedestimate > VelDesired ) {
- // jesli jedzie za szybko do AKTUALNEGO
- if( VelDesired == 0.0 ) {
- // jesli stoj, to hamuj, ale i tak juz za pozno :)
- AccDesired = std::min( AccDesired, -0.85 ); // hamuj solidnie
- }
- else {
- // if it looks like we'll exceed maximum speed start thinking about slight slowing down
- AccDesired = std::min( AccDesired, -0.05 + fAccThreshold );
- // HACK: for cargo trains with high braking threshold ensure we cross that threshold
- if( true == IsCargoTrain
- && fBrake_a0[0] > 0.2 ) {
- AccDesired -= std::clamp( fBrake_a0[ 0 ] - 0.2, 0.0, 0.15 );
- }
- }
+ // look ahead for as long as the brake takes to respond, since that is how long the
+ // present acceleration will last whatever is done with the handle now; a pneumatic
+ // brake applies and releases on its own time scale, which in G is tens of seconds
+ auto const horizon { brake_response_horizon( mvOccupied, BrakeSystem, AbsAccS, fBrake_a0[ 0 ] ) };
+ auto const speedestimate = vel + 3.6 * horizon * AbsAccS;
+ // already below a lower limit ahead, the train has no business speeding up towards it
+ auto holdlimit { VelDesired };
+ if( ( VelNext > 0.0 )
+ && ( VelNext < holdlimit )
+ && ( vel <= VelNext + fVelPlus )
+ && ( ActualProximityDist < std::max( 2.0 * fBrakeDist, 500.0 ) ) ) {
+ holdlimit = VelNext;
+ }
+ // hold a band just below the limit; the steeper the descent the harder the train pulls
+ // away once the brake eases, so the band widens with the gradient (about 3 km/h at 20 per mille)
+ // an electro-pneumatic or electric brake responds at once and needs far less room
+ auto const holdspeed { holdlimit - (
+ BrakeSystem == TBrakeSystem::Pneumatic ?
+ std::clamp( fAccGravity * 15.0, 1.0, 3.0 ) :
+ std::clamp( fAccGravity * 7.5, 1.0, 1.5 ) ) };
+ // the predicted difference is to be made up over the time the brake takes to respond, not at once: with a
+ // slow brake, a demand sized to remove it at once comes on as a deep application, which then takes as
+ // long to let go, and the train ends up well below the speed it was to hold
+ auto const holdgain { std::min( 0.04, 1.0 / ( 3.6 * std::max( 1.0, horizon ) ) ) };
+ if( ( speedestimate > VelDesired )
+ && ( VelDesired == 0.0 ) ) {
+ // jesli stoj, to hamuj, ale i tak juz za pozno :)
+ AccDesired = std::min( AccDesired, -0.85 ); // hamuj solidnie
}
else {
- // stop accelerating when close enough to target speed
+ // one continuous law on both sides of the target, proportional to the predicted
+ // difference: a demand that jumps to the braking threshold above the target and to
+ // full acceleration below it alternates, with a brake that responds in seconds,
+ // between a hard application and a full release
AccDesired = std::min(
AccDesired, // but don't override decceleration for VelNext
- std::lerp( // ease off as you close to the target velocity
- EU07_AI_NOACCELERATION, AccPreferred,
- std::clamp( VelDesired - speedestimate, 0.0, fVelMinus ) / fVelMinus ) );
+ std::clamp(
+ EU07_AI_NOACCELERATION - holdgain * ( speedestimate - holdspeed ),
+ -0.05 + fAccThreshold,
+ std::max( EU07_AI_NOACCELERATION, AccPreferred ) ) );
+ // HACK: for cargo trains with high braking threshold ensure we cross that threshold
+ if( IsCargoTrain
+ && fBrake_a0[0] > 0.2
+ && speedestimate > holdlimit ) {
+ AccDesired -= std::clamp( fBrake_a0[ 0 ] - 0.2, 0.0, 0.15 );
+ }
}
// final tweaks
if( vel > EU07_AI_NOMOVEMENT ) {
// going downhill also take into account impact of gravity
AccDesired -= fAccGravity;
+ // from here on AccDesired is what the brake alone has to deliver, which is what
+ // IncBrake() sizes the handle position for; AbsAccS already contains the gravity,
+ // so the comparisons with it have to add this part back
+ fAccDesiredGravity = fAccGravity;
// HACK: if the max allowed speed was exceeded something went wrong; brake harder
AccDesired -= 0.15 * std::clamp( vel - VelDesired, 0.0, 5.0 );
}
@@ -7724,6 +7942,10 @@ TController::adjust_desired_speed_for_braking_test() {
if( false == AIControllFlag ) { return; } // TBD: add notification about braking test and enable it for human driver as well?
auto const vel { DirectionalVel() };
+ // what the route ahead asks for; the test may brake harder than that, never softer
+ auto const routeaccdesired { AccDesired };
+ auto const routeaccdesiredgravity { fAccDesiredGravity };
+ auto const routeveldesired { VelDesired };
switch( DynamicBrakeTest ) {
case 0: {
@@ -7741,6 +7963,7 @@ TController::adjust_desired_speed_for_braking_test() {
}
case 1: {
AccDesired = EU07_AI_BRAKINGTESTACCELERATION;
+ fAccDesiredGravity = 0.0;
VelDesired = DBT_VelocityBrake;
if( ElapsedTime - DBT_BrakingTime > 1 ) {
ForcePNBrake = true;
@@ -7751,6 +7974,7 @@ TController::adjust_desired_speed_for_braking_test() {
}
case 2: {
AccDesired = EU07_AI_BRAKINGTESTACCELERATION;
+ fAccDesiredGravity = 0.0;
VelDesired = DBT_VelocityBrake;
if( ElapsedTime - DBT_BrakingTime > 2 ) {
DBT_BrakingTime = ElapsedTime;
@@ -7762,6 +7986,7 @@ TController::adjust_desired_speed_for_braking_test() {
}
case 3: {
AccDesired = safe_clamp( -AbsAccS, fAccThreshold * 1.01, fAccThreshold * 1.21 );
+ fAccDesiredGravity = 0.0;
VelDesired = DBT_VelocityBrake;
if( vel <= DBT_VelocityRelease ) {
DynamicBrakeTest = 4;
@@ -7787,6 +8012,14 @@ TController::adjust_desired_speed_for_braking_test() {
break;
}
}
+ // a stop or a restriction coming up while the test runs takes precedence
+ if( ( DynamicBrakeTest >= 1 )
+ && ( DynamicBrakeTest <= 3 )
+ && ( routeaccdesired < AccDesired ) ) {
+ AccDesired = routeaccdesired;
+ fAccDesiredGravity = routeaccdesiredgravity;
+ VelDesired = std::min( VelDesired, routeveldesired );
+ }
}
void
@@ -7875,7 +8108,9 @@ void TController::control_tractive_force() {
(ActualProximityDist > (is_car() ? fMinProximityDist : // cars are allowed to move within min proximity distance
fMaxProximityDist) ? // other vehicle types keep wider margin
true :
- velocity + 1.0 < VelNext) ) {
+ velocity + 1.0 < VelNext
+ // uphill, a train told to hold its speed on the way to a stop needs the power to do it
+ || VelNext == 0.0 && (OrderCurrentGet() & Obey_train) != 0 && AccDesired >= 0.0 && fAccGravity < -0.025 && ActualProximityDist > fMinProximityDist) ) {
// ...jeśli prędkość w kierunku czoła jest mniejsza od dozwolonej o margines...
// within proximity margin don't exceed next scheduled speed, outside of the margin accelerate as you like
// ...to można przyspieszyć
@@ -7890,9 +8125,7 @@ void TController::control_tractive_force() {
cue_action( driver_hint::mastercontrollersetzerospeed );
}
else if( velocity > VelDesired + SpeedCtrlMargin
- || ( fAccGravity < -0.01 ?
- AccDesired < 0.0 :
- AbsAccS > AccDesired + 10.05 )
+ || ( fAccGravity < -0.01 ? is_too_fast_uphill() : AbsAccS > AccDesired + 10.05 )
|| IsAnyCouplerStretched ) {
// jak za bardzo przyspiesza albo prędkość przekroczona
// dodany wyjatek na "pelna w przod"
@@ -7904,6 +8137,13 @@ void TController::control_tractive_force() {
SpeedSet(); // ciągla regulacja prędkości
}
+bool TController::is_too_fast_uphill() const {
+
+ if( AccDesired < 0.0 ) { return true; }
+ // holding the speed uphill on the way to a stop asks for no speeding up
+ return ( VelNext == 0.0 && ActualProximityDist <= fMaxProximityDist && AbsAccS > AccDesired + 0.05 );
+}
+
void TController::increase_tractive_force() {
if( fActionTime < 0.0 ) { return; } // gdy jest nakaz poczekać z jazdą, to nie ruszać
@@ -7926,6 +8166,20 @@ void TController::increase_tractive_force() {
cue_action( driver_hint::tractiveforceincrease );
}
+// how far the deceleration may fall short of the demand before the brake is applied harder. approaching a lower
+// speed, a pneumatic brake given a full step only once that much is missing acts too late: the demand
+// creeps up meanwhile, and in a long G train the next step takes many seconds to act, so half a step is enough
+double
+TController::brake_increase_margin() const {
+
+ if( ( BrakeSystem == TBrakeSystem::Pneumatic )
+ && ( VelNext > 0.0 )
+ && ( VelNext < DirectionalVel() ) ) {
+ return 0.5 * fBrake_a1[ 0 ];
+ }
+ return fBrake_a1[ 0 ];
+}
+
void TController::control_braking_force() {
// don't touch the brakes when compressing buffers during coupling/uncoupling
@@ -7939,6 +8193,23 @@ void TController::control_braking_force() {
cue_action(driver_hint::brakingforcesetzero);
}
+ // AbsAccS is the resultant acceleration, gravity included, so it is compared with the
+ // resultant the AI asks for, not with the brake-only demand AccDesired holds on a descent
+ auto const accdesiredresultant { AccDesired + fAccDesiredGravity };
+
+ // every few seconds, note how far the train falls short of the demand and whether that is getting worse: a brake
+ // still filling, or blocks gripping harder as the train slows down, make up a shortfall on their own, but a
+ // shortfall that keeps growing at a constant handle means the train is falling further behind its braking curve
+ if( ElapsedTime - fShortfallTime >= 3.0 ) {
+ auto const shortfall { AbsAccS - accdesiredresultant };
+ iShortfallGrowth = (
+ ( ( AccDesired < 0.0 ) && ( shortfall > fShortfall + 0.005 ) ) ?
+ iShortfallGrowth + 1 :
+ 0 );
+ fShortfall = shortfall;
+ fShortfallTime = ElapsedTime;
+ }
+
if( is_emu() && !ForcePNBrake ) {
// właściwie, to warunek powinien być na działający EP
// Ra: to dobrze hamuje EP w EZT
@@ -7949,14 +8220,15 @@ void TController::control_braking_force() {
auto const AccMax{ std::min(fBrake_a0[0] + 12 * fBrake_a1[0], mvOccupied->MED_amax) };
auto const accmargin = AccMax > 1.1 * AccDesired && fAccGravity < 0.025 ?
0.05 : 0.0;
- if( AccDesired < accthreshold // jeśli hamować - u góry ustawia się hamowanie na fAccThreshold
- && ( AbsAccS > AccDesired + accmargin
- || BrakeCtrlPosition < 0 ) ) {
+ if( ( AccDesired < accthreshold // jeśli hamować - u góry ustawia się hamowanie na fAccThreshold
+ && ( AbsAccS > accdesiredresultant + accmargin
+ || BrakeCtrlPosition < 0 ) )
+ || ( fAccGravity < -0.05 && velocity < -0.1 && AccDesired < 0.0 ) ) { // also brake if uphill and slipping back
// hamować bardziej, gdy aktualne opóźnienie hamowania mniejsze niż (AccDesired)
cue_action( driver_hint::brakingforceincrease );
}
else if( OrderCurrentGet() != Disconnect ) { // przy odłączaniu nie zwalniamy tu hamulca
- if( AbsAccS < AccDesired - 0.05 ) {
+ if( AbsAccS < accdesiredresultant - 0.05 ) {
// jeśli opóźnienie większe od wymaganego (z histerezą) luzowanie, gdy za dużo
// TBD: check if the condition isn't redundant with the DecBrake() code
if( BrakeCtrlPosition >= 0 && VelDesired > 0.0 ) {
@@ -7971,9 +8243,12 @@ void TController::control_braking_force() {
} // type & dt_ezt
else {
// a stara wersja w miarę dobrze działa na składy wagonowe
- if( (AccDesired < fAccGravity - 0.1 && AbsAccS > AccDesired + fBrake_a1[0] // regular braking
- || fAccGravity < -0.05 && velocity < -0.1) &&
- (fBrakeTime < 0.0 || AccDesired < fAccGravity - 0.5 || BrakeCtrlPosition <= 0) ) { // also brake if uphill and slipping back
+ if( (accdesiredresultant < fAccGravity - 0.1 && AbsAccS > accdesiredresultant + brake_increase_margin() // regular braking
+ || fAccGravity < -0.05 && velocity < -0.1) && // also brake if uphill and slipping back
+ (fBrakeTime < 0.0
+ // the size of the brake demand decides whether to skip the reaction time, so the brake-only part
+ // (on a descent a heavy train needs its brake built up at once, the gravity does not help it slow)
+ || AccDesired < fAccGravity - 0.5 || BrakeCtrlPosition <= 0) ) {
// u góry ustawia się hamowanie na fAccThreshold
// jeśli upłynął czas reakcji hamulca, chyba że nagłe albo luzował
// TODO: check whether brake delay variable still has any purpose
@@ -7981,7 +8256,21 @@ void TController::control_braking_force() {
// Ra: ten czas należy zmniejszyć, jeśli czas dojazdu do zatrzymania jest mniejszy
(3.0 + 0.5 * ((mvOccupied->BrakeDelayFlag > bdelay_G ? mvOccupied->BrakeDelay[1] : mvOccupied->BrakeDelay[3]) - 3.0)) * 0.5); // Ra: tymczasowo, bo przeżyna S1
}
- if ( AccDesired < fAccGravity - 0.05 && AccDesired - fBrake_a1[0] * 0.51 - AbsAccS > 0.05 && OrderCurrentGet() != Disconnect && VelDesired > 0.0 ) {
+ else if( ( BrakeSystem == TBrakeSystem::Pneumatic )
+ && ( AccDesired < 0.0 )
+ && ( BrakeCtrlPosition > 0.0 )
+ && ( fShortfall > 0.02 )
+ && ( iShortfallGrowth >= 2 )
+ && ( fBrakeHeldTime > brake_response_horizon( mvOccupied, BrakeSystem, 1.0, fBrake_a0[ 0 ] ) ) ) {
+ // the train has been falling further behind the demand for a while at a handle left alone for longer than
+ // the brake takes to respond: a small step now, while it still is small, rather than the deep one the
+ // margin above would allow only once the train is far behind
+ cue_action( driver_hint::brakingforceincrease );
+ iShortfallGrowth = 0;
+ }
+ if ( (accdesiredresultant < fAccGravity - 0.05
+ || AccDesired >= EU07_AI_NOACCELERATION) // no braking asked for, yet the train is slowing down more than that
+ && accdesiredresultant - fBrake_a1[0] * 0.51 - AbsAccS > 0.05 && OrderCurrentGet() != Disconnect && VelDesired > 0.0 ) {
// jak hamuje, to nie tykaj kranu za często
// yB: luzuje hamulec dopiero przy różnicy opóźnień rzędu 0.2
// przy odłączaniu nie zwalniamy tu hamulca
@@ -8057,8 +8346,11 @@ void TController::control_releaser() {
}
}
// wyluzuj lokomotywę, to szybciej ruszymy
+ // this rule is for starting off, so only below 1 km/h: at speed, just after an application, it would vent
+ // the control reservoir of a locomotive that may have to brake again before the pipe has refilled it
if( mvOccupied->BrakePress > 0.4
- && mvOccupied->Hamulec->GetCRP() > 4.9 ) {
+ && mvOccupied->Hamulec->GetCRP() > 4.9
+ && mvOccupied->Vel < 1.0 ) {
actuate = true;
}
// keep engine brakes released during coupling/uncoupling
diff --git a/vehicle/Driver.h b/vehicle/Driver.h
index f88e115ac..f1975fca6 100644
--- a/vehicle/Driver.h
+++ b/vehicle/Driver.h
@@ -311,6 +311,8 @@ private:
double DirectionalVel() const {
return mvOccupied->Vel * sign( iDirection * mvOccupied->V ); }
+ double brake_increase_margin() const;
+
void update_timers( double const dt );
void update_logs( double const dt );
void determine_consist_state();
@@ -358,7 +360,14 @@ private:
void control_motor_connectors();
void control_tractive_force();
void increase_tractive_force();
+ bool is_too_fast_uphill() const;
void control_braking_force();
+ // how far short of a stop the AI aims: in train running the middle of the 8-15 m window before a signal,
+ // otherwise (a W4 stop point, shunting, coupling) half the minimum proximity distance, as before
+ double stopping_margin() const {
+ return ( ( OrderCurrentGet() & Obey_train ) != 0 ) && !IsAtPassengerStop ?
+ fMinProximityDist + 3.5 :
+ 0.5 * fMinProximityDist; }
void apply_independent_brake_only();
void check_route_ahead( double const Range );
void check_route_behind( double const Range );
@@ -393,6 +402,11 @@ private:
double AccDesired = 0.0; // przyspieszenie, jakie ma utrzymywać (<0:nie przyspieszaj,<-0.1:hamuj)
double VelDesired = 0.0; // predkość, z jaką ma jechać, wynikająca z analizy tableki; <=VelSignal
double fAccDesiredAv = 0.0; // uśrednione przyspieszenie z kolejnych przebłysków świadomości, żeby ograniczyć migotanie
+ double fBrakeHeldTime = 0.0; // czas od ostatniego przestawienia kranu hamulca zasadniczego
+ double fShortfall = 0.0; // how far the train fell short of the braking demand at the last check [m/s2]
+ double fShortfallTime = 0.0; // when that was checked
+ int iShortfallGrowth = 0; // how many checks in a row the shortfall has grown
+ double fAccDesiredGravity = 0.0; // część AccDesired odjęta na spadku jako kompensacja pochylenia; AbsAccS zawiera ją już sam
double VelforDriver = -1.0; // prędkość, używana przy zmianie kierunku (ograniczenie przy nieznajmości szlaku?)
double VelSignal = 0.0; // ograniczenie prędkości z kompilacji znaków i sygnałów // normalnie na początku ma stać, no chyba że jedzie
double VelLimit = -1.0; // predkość zadawana przez event jednokierunkowego ograniczenia prędkości // -1: brak ograniczenia prędkości