diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h
index 16bb207f..e6d6296a 100644
--- a/McZapkie/MOVER.h
+++ b/McZapkie/MOVER.h
@@ -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*/
diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp
index 20761bac..6e38ad14 100644
--- a/McZapkie/Mover.cpp
+++ b/McZapkie/Mover.cpp
@@ -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();
diff --git a/McZapkie/hamulce.cpp b/McZapkie/hamulce.cpp
index c0a7d2a0..da0a9811 100644
--- a/McZapkie/hamulce.cpp
+++ b/McZapkie/hamulce.cpp
@@ -968,6 +968,28 @@ double TESt::BVs(double const BCP) const
return 0;
}
+///
+/// Refills the auxiliary reservoir (ZP) from the valve pre-chamber through the
+/// check valve of the slide valve, opened as far as 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().
+///
+/// Brake cylinder (or impulse-chamber) pressure.
+/// Time step [s].
+/// Volume taken into the auxiliary reservoir, 0.96 of it drawn from the brake pipe.
+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;
+}
+
///
/// One-step Oerlikon ESt distributor advance: runs CheckState/CheckReleaser,
/// integrates the ZS <-> PG, ZP <-> cylinder, ZP <-> 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);
diff --git a/McZapkie/hamulce.h b/McZapkie/hamulce.h
index 37b9b6a4..b9a5ffd1 100644
--- a/McZapkie/hamulce.h
+++ b/McZapkie/hamulce.h
@@ -620,6 +620,15 @@ class TESt : public TBrake
/// Brake cylinder pressure.
/// Dimensionless opening coefficient.
double BVs(double BCP) const; // napelniacz pomocniczego
+ ///
+ /// Refills the auxiliary reservoir (ZP) from the valve pre-chamber through the
+ /// check valve of the slide valve, opened as far as says, while
+ /// the pre-chamber is above the reservoir. The flows wait for the caller's Act().
+ ///
+ /// Brake cylinder (or impulse-chamber) pressure for .
+ /// Time step [s].
+ /// Volume taken into the auxiliary reservoir, 0.96 of it drawn from the brake pipe.
+ double FillBrakeRes(double BCP, double dt); // napelnianie pomocniczego z rozdzielacza
/// Vents the valve, brake and control reservoirs to zero.
void ForceEmptiness() override; // wymuszenie bycia pustym
diff --git a/tests/unit/test_brake_handles.cpp b/tests/unit/test_brake_handles.cpp
index 51c25243..e6b43547 100644
--- a/tests/unit/test_brake_handles.cpp
+++ b/tests/unit/test_brake_handles.cpp
@@ -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);
+}
diff --git a/tests/unit/test_brakes.cpp b/tests/unit/test_brakes.cpp
index 83416807..e12694f3 100644
--- a/tests/unit/test_brakes.cpp
+++ b/tests/unit/test_brakes.cpp
@@ -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);
+ }
+}