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); + } +}