18
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-15 18:44:12 +01:00

fix: EN57 brakes hauled with the brake hose alone, Knorr EP brake without control power

eu07/maszyna#87: the EP2 (and EP1) valve charged its auxiliary reservoir only
from the main reservoir pipe. A dead EN57 hauled by a locomotive coupled with
the hook and the brake hose alone ("3") never charged it, its control
reservoir stopped at about 2.5 bar and the unit didn't brake. The valve now
refills the auxiliary reservoir from the brake pipe through the slide valve's
check valve, as every ESt does (TESt::FillBrakeRes, shared with TESt::GetPF,
which stays bit for bit the same). With the main reservoir pipe charged the
auxiliary reservoir stays above the brake pipe and nothing changes; the
reservoir now also takes air from the brake pipe while it is below it, e.g.
at start-up before the compressor has charged the main reservoir pipe.

eu07/maszyna#134: the EP brake of the Knorr St113 handle (EN57-001) worked
with the reverser in neutral, the battery off or the EP fuse switched off
(Ctrl+Z). It now needs the control powered, as the Oerlikon handle's does
(EPBrakeControlPowered: battery, reverser out of neutral, EP fuse), and an
EP brake switched on drops out (holding the cylinder pressure) when the
control loses power. The positions of the EP release and brake were already
moved by 810b7ad07; a test pins them.

No change to game data needed.
This commit is contained in:
jerrrrycho
2026-11-08 04:25:34 +01:00
parent 68fdf1efdf
commit 87f1d960a0
6 changed files with 200 additions and 11 deletions

View File

@@ -2413,6 +2413,8 @@ class TMoverParameters
bool BrakeReleaser(int state);
bool UniversalBrakeButton(int button, int state); /*uniwersalny przycisk hamulca*/
bool SwitchEPBrake(int state);
// whether the EP brake control of the cab has power: battery, the reverser out of neutral and the EP fuse on
bool EPBrakeControlPowered() const;
bool AntiSlippingButton(void); /*! reczny wlacznik urzadzen antyposlizgowych*/
/*funkcje dla ukladow pneumatycznych*/

View File

@@ -4161,7 +4161,8 @@ bool TMoverParameters::SwitchEPBrake(int state)
OK = false;
if (BrakeHandle == TBrakeHandle::St113 && CabOccupied != 0)
{
if (state > 0)
// the handle works the EP contactors pneumatically, their supply is cut as the Oerlikon handle's is
if (state > 0 && EPBrakeControlPowered())
EpForce = Handle->GetEP(); // TODO: przetlumaczyc
else
EpForce = 0;
@@ -4173,6 +4174,11 @@ bool TMoverParameters::SwitchEPBrake(int state)
return OK;
}
bool TMoverParameters::EPBrakeControlPowered() const
{
return Power24vIsAvailable && DirActive != 0 && EpFuse;
}
// *************************************************************************************************
// Q: 20160711
// zwiększenie ciśnienia hamowania
@@ -4706,7 +4712,7 @@ void TMoverParameters::UpdatePipePressure(double dt)
if ((BrakeHandle == TBrakeHandle::FVel6 || BrakeHandle == TBrakeHandle::FVE408) && CabOccupied != 0)
{
if (Power24vIsAvailable && DirActive != 0 && EpFuse) // tu powinien byc jeszcze bezpiecznik EP i baterie -
if (EPBrakeControlPowered()) // tu powinien byc jeszcze bezpiecznik EP i baterie -
// temp = (Handle as TFVel6).GetCP
EpForce = Handle->GetEP();
else
@@ -4725,6 +4731,11 @@ void TMoverParameters::UpdatePipePressure(double dt)
// Ra 2014-11: na tym się wysypuje, ale nie wiem, w jakich warunkach
SendCtrlToNext("Brake", EpForce, CabActive);
}
else if (BrakeHandle == TBrakeHandle::St113 && CabOccupied != 0 && std::abs(EpForce) > 0.001 && !EPBrakeControlPowered())
{
// the EP brake switched on with the button drops out with the control's supply
SwitchEPBrake(0);
}
Pipe->Act();
PipePress = Pipe->P();

View File

@@ -968,6 +968,28 @@ double TESt::BVs(double const BCP) const
return 0;
}
/// <summary>
/// Refills the auxiliary reservoir (ZP) from the valve pre-chamber through the
/// check valve of the slide valve, opened as far as <see cref="BVs"/> says for the
/// given cylinder (or impulse-chamber) pressure, while the pre-chamber is more
/// than 0.05 bar above the reservoir. The flows wait for the caller's Act().
/// </summary>
/// <param name="BCP">Brake cylinder (or impulse-chamber) pressure.</param>
/// <param name="dt">Time step [s].</param>
/// <returns>Volume taken into the auxiliary reservoir, 0.96 of it drawn from the brake pipe.</returns>
double TESt::FillBrakeRes(double const BCP, double const dt)
{
double const BVP{BrakeRes->P()};
double const VVP{ValveRes->P()};
double const temp{BVs(BCP)};
double dv{0.0};
if (VVP - 0.05 > BVP)
dv = PF(BVP, VVP, 0.02 * SizeBR * temp / 1.87) * dt;
BrakeRes->Flow(dv);
ValveRes->Flow(-0.04 * dv);
return dv;
}
/// <summary>
/// One-step Oerlikon ESt distributor advance: runs CheckState/CheckReleaser,
/// integrates the ZS &lt;-&gt; PG, ZP &lt;-&gt; cylinder, ZP &lt;-&gt; pre-chamber and
@@ -1021,15 +1043,7 @@ double TESt::GetPF(double const PP, double const dt, double const Vel)
BrakeCyl->Flow(-dv);
// przeplyw ZP <-> rozdzielacz
temp = BVs(BCP);
// if(BrakeStatus and b_hld)=b_off then
if (VVP - 0.05 > BVP)
dv = PF(BVP, VVP, 0.02 * SizeBR * temp / 1.87) * dt;
else
dv = 0;
BrakeRes->Flow(dv);
dV1 = dV1 + dv * 0.96;
ValveRes->Flow(-0.04 * dv);
dV1 = dV1 + FillBrakeRes(BCP, dt) * 0.96;
// przeplyw PG <-> rozdzielacz
dv = PF(PP, VVP, 0.01) * dt;
ValveRes->Flow(-dv);
@@ -1191,6 +1205,10 @@ double TEStEP2::GetPF(double const PP, double const dt, double const Vel)
ValveRes->Flow(-0.04 * dv);
dV1 = dV1 - 0.96 * dv;
// przeplyw ZP <-> rozdzielacz: as in every ESt, the auxiliary reservoir also charges from the brake pipe,
// not from the main reservoir pipe alone, so a unit hauled with the brake hose alone charges and brakes
dV1 = dV1 + FillBrakeRes(BCP, dt) * 0.96;
// hamulec EP
EPCalc(dt);

View File

@@ -620,6 +620,15 @@ class TESt : public TBrake
/// <param name="BCP">Brake cylinder pressure.</param>
/// <returns>Dimensionless opening coefficient.</returns>
double BVs(double BCP) const; // napelniacz pomocniczego
/// <summary>
/// Refills the auxiliary reservoir (ZP) from the valve pre-chamber through the
/// check valve of the slide valve, opened as far as <see cref="BVs"/> says, while
/// the pre-chamber is above the reservoir. The flows wait for the caller's Act().
/// </summary>
/// <param name="BCP">Brake cylinder (or impulse-chamber) pressure for <see cref="BVs"/>.</param>
/// <param name="dt">Time step [s].</param>
/// <returns>Volume taken into the auxiliary reservoir, 0.96 of it drawn from the brake pipe.</returns>
double FillBrakeRes(double BCP, double dt); // napelnianie pomocniczego z rozdzielacza
/// <summary>Vents the valve, brake and control reservoirs to zero.</summary>
void ForceEmptiness() override; // wymuszenie bycia pustym

View File

@@ -4,6 +4,7 @@
#include "golden.h"
#include "real_vehicles.h"
#include "drive.h"
#include "consist.h"
#include "McZapkie/hamulce.h"
@@ -152,3 +153,105 @@ TEST_CASE("brake handles: the overload and the high pressure buttons of the MHZ
trace.check();
}
}
// eu07/maszyna#134: the Knorr St113 of the EN57-001 releases the EP brake in the running position (bh_RP, Num 6)
// and applies it one position on (Num 8), short of the lap position (Num 5); the positions are those the keys
// select, and the EP brake works only with the control of the cab powered: the battery, the reverser out of
// neutral and the EP fuse on (Ctrl+Z), as the Oerlikon handle's
namespace {
// the front car of the EN57-001 driven from its cab, with the motor car behind it, which has the battery of the two;
// air in the reservoirs, the battery on
void ready_knorr_ep(test::consist &Unit) {
Unit.add(definition::fiz("en57_v1/6bai"), 1);
Unit.add(definition::fiz("en57_v1/5bgb"));
auto &cab{Unit[0]};
REQUIRE(cab->BrakeHandle == TBrakeHandle::St113);
cab->BatterySwitch(true);
cab->CabActivisation();
cab.hold_brake_pipe(5.0);
cab->BrakeLevelSet(cab->Handle->GetPos(bh_RP));
Unit.run(60.0);
}
// the EP brake button pressed in the given handle position, held for Seconds; the cylinder pressure of the cab car
double ep_brake(test::consist &Unit, double Position, double Seconds) {
auto &cab{Unit[0]};
cab->BrakeLevelSet(Position);
Unit.run(0.1);
cab->SwitchEPBrake(1);
Unit.run(Seconds);
return cab->BrakePress;
}
} // namespace
TEST_CASE("brake handles: the positions of the Knorr St113 EP brake") {
test::consist unit;
ready_knorr_ep(unit);
auto &cab{unit[0]};
auto &handle{*cab->Handle};
auto const ep_at{[&unit, &cab, &handle](double Position) {
cab->BrakeLevelSet(Position);
unit.run(0.1);
return handle.GetEP();
}};
// the keys: Num 6 running, Num 8 the first position on, Num 5 the middle of the handle
CHECK(handle.GetPos(bh_RP) == doctest::Approx(0.0));
CHECK(ep_at(0.0) == doctest::Approx(-1.0)); // EP release
CHECK(ep_at(1.0) == doctest::Approx(1.0)); // EP brake
CHECK(ep_at(cab->BrakeCtrlPosNo / 2) == doctest::Approx(0.0)); // lap, Num 5
CHECK(ep_at(handle.GetPos(bh_FB)) == doctest::Approx(0.0));
CHECK(ep_at(handle.GetPos(bh_EPR)) == doctest::Approx(-1.0));
CHECK(ep_at(handle.GetPos(bh_EPB)) == doctest::Approx(1.0));
}
TEST_CASE("brake handles: the Knorr St113 EP brake needs the control of the cab powered") {
for (std::string const cut : {"reverser in neutral", "EP fuse off", "battery off"}) {
CAPTURE(cut);
test::consist unit;
ready_knorr_ep(unit);
auto &cab{unit[0]};
cab->DirectionForward();
unit.run(1.0);
REQUIRE(cab->EPBrakeControlPowered());
if (cut == "reverser in neutral") {
cab->DirectionBackward();
}
else if (cut == "EP fuse off") {
cab->EpFuseSwitch(false);
}
else {
cab->BatterySwitch(false);
}
unit.run(1.0);
REQUIRE_FALSE(cab->EPBrakeControlPowered());
// the button in the EP brake position does nothing
CHECK(ep_brake(unit, 1.0, 5.0) < 0.1);
CHECK(cab->EpForce == doctest::Approx(0.0));
cab->SwitchEPBrake(0);
}
test::consist unit;
ready_knorr_ep(unit);
auto &cab{unit[0]};
cab->DirectionForward();
unit.run(1.0);
// powered, the EP brake applies
auto const applied{ep_brake(unit, 1.0, 2.0)};
CHECK(applied > 0.5);
// the reverser put to neutral with the button still held: the EP brake drops out and holds what it applied
cab->DirectionBackward();
unit.run(0.1);
CHECK(cab->EpForce == doctest::Approx(0.0));
auto const held{cab->BrakePress};
unit.run(5.0);
CHECK(cab->BrakePress < held + 0.05);
CHECK(cab->BrakePress > 0.2);
// and doesn't release either, until the control is powered again
CHECK(ep_brake(unit, 0.0, 5.0) > 0.2);
cab->SwitchEPBrake(0);
cab->DirectionForward();
unit.run(0.1);
CHECK(ep_brake(unit, 0.0, 10.0) < 0.1);
}

View File

@@ -1,6 +1,7 @@
#include "stdafx.h"
#include "doctest.h"
#include "vehicle.h"
#include "consist.h"
using test::definition;
using test::vehicle;
@@ -91,3 +92,48 @@ TEST_CASE("brakes: service braking stops a heavier wagon too") {
wagon.run(40.0 /* s */, test::track{0.15 /* friction */, 0.0 /* gradient */});
CHECK(std::abs(wagon.speed()) < 0.1);
}
// eu07/maszyna#87: the ESt4d of the EN57's EP2 valve charged its auxiliary reservoir from the main reservoir pipe
// alone, so a dead unit hauled by a locomotive coupled with the hook and the brake hose ("3") never charged it,
// its control reservoir stopped half way, and it didn't brake. Like every ESt, it charges from the brake pipe too
TEST_CASE("brakes: a dead EN57 hauled with the brake hose alone charges from the brake pipe and brakes") {
test::consist train;
auto &locomotive{train.add(definition::fiz("sm42_v1/6d1"))};
train.add(definition::fiz("en57_v1/5bgb"), 0, coupling::coupler | coupling::brakehose);
train.add(definition::fiz("en57_v1/6ba_840"));
train.add(definition::fiz("en57_v1/5bgaiii_led_spot"));
// a unit standing dead: no air anywhere, nothing in its main reservoir pipe
for (std::size_t i = 1; i < train.size(); ++i) {
auto &car{train[i].mover()};
REQUIRE(car.BrakeValve == TBrakeValve::EP2);
car.Hamulec->ForceEmptiness();
car.Pipe->CreatePress(0.0);
car.Pipe->Act();
car.Pipe2->CreatePress(0.0);
car.Pipe2->Act();
car.CompressedVolume = 0.0;
}
locomotive.hold_brake_pipe(5.0);
train.run(900.0);
for (std::size_t i = 1; i < train.size(); ++i) {
CAPTURE(i);
auto &car{train[i].mover()};
REQUIRE(car.ScndPipePress < 0.1); // no main reservoir pipe
CHECK(car.Hamulec->GetBRP() > 4.8);
CHECK(car.Hamulec->GetCRP() > 4.8);
CHECK(car.BrakePress < 0.1);
}
// a full service application brakes the unit, and it releases again
locomotive.hold_brake_pipe(3.5);
train.run(20.0);
for (std::size_t i = 1; i < train.size(); ++i) {
CAPTURE(i);
CHECK(train[i]->BrakePress > 2.0);
}
locomotive.hold_brake_pipe(5.0);
train.run(60.0);
for (std::size_t i = 1; i < train.size(); ++i) {
CAPTURE(i);
CHECK(train[i]->BrakePress < 0.1);
}
}