17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-08 05:10:41 +01:00

Test/core unit coverage (#235)

* test: real vehicles and golden traces in the unit tests

The game's own vehicles (dynamic/ of the release used for the tests) as
JSON of what the game's vehicle loader reads of their .fiz files, includes
expanded: test_fiz.cpp converts them on demand and checks the loader reads
the same sections, values and rows as of the game's file (fiz.h). Chosen
to cover every engine type, brake valve, brake handle, local brake,
coupler type, door type and brake delay set the game data uses.

Golden traces (golden.h) characterise the simulation, compared within a
relative tolerance or exactly, re-recorded with
EU07_UNIT_TESTS_UPDATE_GOLDEN=1:
- the parameters of every real vehicle as loaded,
- the brakes of every real vehicle through charge, service steps,
  release, overcharge and emergency, on every brake delay setting,
- every position of every driver's and local brake handle,
- every powered vehicle started up the way the AI does it, driven to
  full power and braked to a stop (drive.h),
- the real electric units and a locomotive with wagons driven whole, as
  trains of coupled vehicles stepped the way the game steps them
  (consist.h),
- scenarios of the controls (scenario.h): doors, reversing and cab
  change, slip and sand, emergency stops, releasers, the ep and the
  electrodynamic brake, heating, loading, a collision, and frames of
  several physics iterations.

* test: commands passed between vehicles

Every command RunCommand knows run on every real vehicle and recorded,
commands travelling through a train with a vehicle facing backwards, a
coupling without the control cable, and loading and unloading by commands.

* test: every switch of the cab, sent to the next vehicle

Each switch sending its state to the train, each way and to each range,
with the controllers and direction among them, on every real vehicle
driving another of its kind, recorded for both.

* test: the switches of the cab worked on a pair under the catenary

The same switches with two seconds for the vehicles to act on each, so
the pantographs rise, the main switch closes and the auxiliaries start.

* test: gauges of trains in multiple and the damage descriptions

The ammeters and the brake force the AI expects, recorded on the electric
units and locomotives in multiple driven, the damage described on the
driver's panel and ammeters showing the current of controlled vehicles.

* test: give load commands their place, as the game does

The loading test set its commands with SetInternalCommand, which leaves
TCommand::Location as it is: uninitialised in a fresh vehicle, so the
distance check of Load= and UnLoad= read garbage and the test failed
now and then. The game gives them through PutCommand with the place of
the platform, and so do the tests now.

* test: brake valves and handles no vehicle of the game data has

Fitted to a wagon and to an EU07: the valve variants and handles the
loader knows that no real vehicle uses, the CV1 ones and the test handle
among them, and a name the loader doesn't know.

* test: braking from speed with the automatic ED brake, and a draisine

Trains with the EP09, the E6ACT and the EW58 braked by ep, light and full
service applications from speed, and the draisine pumped from rest and
rolling. Only the E6ACT brings its electrodynamic brake in here.

* test: the EP09's electrodynamic brake in service braking held to a stop

start_up() took a vehicle for ready once its line breaker was closed and
its reservoir full, which a train made already moving is at once: the
EP09's manually started converter was never switched on, so there was
no 110 V and no electrodynamic brake. It now waits for the converter.

The EP09 with wagons is braked from 120 km/h by light, medium, high and
full service applications held to a stop: the motors brake, the air of
its cylinders is let out and given back as the train slows down. The
flag of the electrodynamic brake stays on at a standstill below 2.1 bar.

C strings given to CAPTURE are shown as text.

* test: the converter switched on before a train is ready, the EW58's ep-staged electrodynamic brake

start_up() called a train ready as soon as its line breaker closed and the air was there, so the converter
of a unit having it in another car than the cab (the EW58's middle car) was never switched on, and its
electrodynamic brake, gated by the 110 V, never came in. The converter and the compressor now always get
switched on first. The EW58 brakes electrically by the stages of the ep brake, not by the pipe alone.

* test: the ET42 and the 214M railbus among the real vehicles

The ET42's halves (its own series motor branch: the motors in series at the high current setting) and the
214MA, a diesel railbus with the hydraulic torque converter, the retarder and the controller of an induction
motor drive, converted from the game data and recorded in every sweep of the real vehicles.

* test: the wagons' hand brakes released with the train's brakes; the 754, SM31 and SU46

release_brakes() released the hand brake of the driver's vehicle only, so every wagon set up braked kept its
hand brake on and got dragged along. It now releases the hand and spring brakes of the whole train, as
TController::AutoRewident() does when the AI takes it over, and the traces of the trains with wagons are
recorded anew.

The 754, the SM31 and the SU46 join the real vehicles for the kinds of the diesel-electrics' field shunting
relays the others lack (RelayType 0, 1 and 46).

* test: traction on a lost and too high catenary voltage, the ET42, the diesel-electrics' shunting relays

The line breaker opened by the voltage relays of a series motor locomotive and by the limits of an induction
motor one. The ET42 at the high current setting, which keeps its halves' motors in series, and its switched
electrodynamic brake, whose current trips the overload relay from the top speed of the low setting. The field
shunting of the diesel-electrics through each kind of their relays, at full power and notched back.

* test: the EW58's electrodynamic brake stages, the EU47's speed control and braking at the voltage's limit

The stages of the EW58's motor brake set by the positions of its ep handle, the first one alone over
100 km/h. The EU47's second controller holding, raising and lowering the speed set, its local brake setting
the flag of the electrodynamic brake without the motors braking, and its brake feeding back less current into
a catenary over the drive's MaxV.

* test: diesel engines too cold and too hot to start, the 214M railbus's controller and retarder

The heat of the diesel engines: at -10 degrees the ones with the lowest water temperature set don't start
until the heater warms them, at 88 degrees the ones with a second water circuit don't start while it cools,
and the SM42 run hot at full power is kept under its highest temperature by its fans and shutters. The 214M
railbus's controller from the air brake through the retarder and neutral to power, its torque converter's
lock-up and its gearbox.

The controller helpers of the traction tests (full power, waiting for the line contactors, and notching back)
move to test::scenario.

* test: couplers coupled, uncoupled, broken and colliding; loads assigned, loaded and unloaded

Coupling by hand touching or apart and of the same kind or not, with the sounds of what changes; uncoupling a
screw coupler pressed together, stretched, with the hoses alone or broken; the automatic couplers of electric
units coupling on contact, without the control cable between two kinds; a coupler broken with its brake
hose; collisions over 15 km/h derailing. The pantographs set up by the pantstate load, a load of one kind at
a time up to the capacity, and the steps of loading and unloading.

* test: the battery drained and charged, the pantographs' tank and pressure switch, the 214M's speed control

The EU07's battery drained without the converter by the lights and the small compressor until it opens the
line breaker and nothing starts, charged by the converter, and switched off. The pantographs on their own
leaking tank until the pressure switch opens the line breaker, and the small compressor pumping it up and
priming the switch. The 214M railbus's speed control set by its buttons, steps and power limit, braking down
with the retarder, and standing by.

* test: the MHZ handles' buttons, the valves at speed, every brake material, the NESt3's relays

The overload and the high pressure buttons of the MHZ handles. The KE and the LSt at speed in the R and P
settings, under the anti-slip request and the releaser button. Every material of the brake shoes and disks
the loader takes, braking a wagon from speed, the ones no game vehicle has among them. The CV1_R and the
Oerlikon valve's relays set by its name (-ED, AL2 without ESt3) fitted to a wagon.

* test: the loader's lists and lines: the 218M, SA134 and 22WEc, a compressor list, Pmax by speed, skipped lines

The 218M (V2NList), the SA134 (HTCList) and the Elf 22WEc (ffBrakeList) join the real vehicles for the lists
the others lack. Definitions made by the tests for what no game file has: a compressor list, whose table of
whole numbers reads a factor of 0.9 as 0; the compressor's limits apart for the rear cab; the highest power
of an induction motor drive by the speed, extrapolated past the table; and the lines the loader skips or
trims (comments, a hash anywhere, leading spaces, carriage returns).

* test: what the cab and the AI call on a vehicle: gauges of the train, the split ED brake, spring brake, cut-off

The engine rotation of up to three vehicles in multiple and the lamps of the overload relay and the starting
resistors read through the control cable; the engine's idle speed and the ET42's second controller shown in
its dynamic brake; the controller of the electrodynamic brake apart from the local brake, which no game
vehicle has yet; the spring brake shut off and released, the ep brake's fuse and the handle stepped by the AI;
damaged motors cut off; the cab activated by itself; a road vehicle moved across a road, for which the tests'
track gets a width; and the lamps and the buzzer of the security system left unattended.

* test: the EP05 and its camshaft among the real vehicles, the headlights' colour and brightness

The EP05, whose camshaft closes the line breaker and the line contactors by the camshaft's own position,
converted from the game data and recorded in every sweep of the real vehicles. The Headlights section, which
only the 214M's own file has, in a definition made by the test.

* test: what the unit tests reach of the simulation core, what's dead or left, and what they found

COVERAGE.md: the blocks of Mover.cpp, hamulce.cpp, Oerlikon_ESt.cpp and friction.cpp the tests run, the code
nothing in the game runs, the code only data no game vehicle has would run, what's reachable and left, and
the behaviour the tests found that may deserve a look.

* test: check invariants of the physics on every step of every golden trace

Rules any vehicle keeps whatever a test does (no NaN, cylinders and pipes
within their limits, no current with the line breaker or contactors open,
no force without power, distance as the speed runs, no acceleration over
5 g), checked on every step, with no comparison to a recorded file. The
traces where a test or a game vehicle breaks one are excused with the
reason; the vehicles' cases are in the findings of COVERAGE.md.

* test: key figures of every golden trace, per part of the run

Duration, speeds, distance, time to stop, the brake pipe's lowest
pressure and time to recover, the peaks of the cylinders, the motor
current and the tractive force, for each part between two events, in
data/golden/<name>.figures. Compared within 0.5% or the last decimal, so
they hold on any compiler, and an intended change of behaviour shows in
a few readable lines. Traces sampled by hand keep none.

* test: keep the golden samples out of the repository, recorded on the code before a change

The samples of the golden traces are 29 MB and only mean something against
the code before the change they check, so they're recorded locally before it
(EU07_UNIT_TESTS_UPDATE_GOLDEN=1) and compared after it, and ignored by git.
The key figures and the invariants stay, and are what the CI checks.

Without samples recorded a trace compares only its key figures, but with
EU07_UNIT_TESTS_GOLDEN_EXACT=1, the check of a change which shouldn't alter
anything, it fails, so the check can't pass by having nothing to compare.

* fix: draw the noise of the control pipe pressure in one order on every compiler

The order of the two Random(10) calls in 'Random(10) - Random(10)' is
unspecified: GCC draws them the other way round than MSVC Release, which
mirrored the control pipe pressure around 5 bar and failed 32 unit tests
on the Linux CI. The draws are sequenced in the order of MSVC Release,
which the golden traces were recorded with.

* test: count the start-up rounds with an integer, not a double (SonarCloud S2193)

* test: behaviour in CHECKs, the key figures out of the repository

The key figures of the golden traces are recorded with their samples on the code
before a change, and compared after it, instead of being kept: a trace without
them checks the invariants and its test's CHECKs, which is what the CI runs.

The scenarios of the controls state what they show in CHECKs, and the runs of
every real vehicle what holds for each of them. Writing them showed scenarios
which didn't do what they were named for: the cab change stopped in the engine
room, the slip runs put the controller to full power at once and never got the
line contactors closed, and the train stopped by the alarm chain never got its
pipe charged again. They do it now; the rest went to the findings.

* test: the SonarCloud findings in the unit tests

std::format for the texts put together, emplace_back, const references, string_view parameters, std::array and std::vector for the C arrays, the ranges algorithms, names no longer shadowing the coupler ends, comments in the empty loops; start_up(), trace::check() and the converter of the definitions split up. BrakeForceR() is const, which it always was in effect, so the gauges test needs no const_cast. The golden traces compare bit for bit.
This commit is contained in:
jerrrrycho
2026-11-07 12:37:12 +01:00
committed by GitHub
parent bb009b0499
commit 466cbbbfa1
101 changed files with 8298 additions and 116 deletions

View File

@@ -2437,7 +2437,7 @@ class TMoverParameters
double Adhesive(double staticfriction) const;
double TractionForce(double dt);
double FrictionForce() const;
double BrakeForceR(double ratio, double velocity);
double BrakeForceR(double ratio, double velocity) const;
double BrakeForceP(double press, double velocity) const;
double BrakeForce(const TTrackParam &Track);
double CouplerForce(int const End, double dt);

View File

@@ -5144,7 +5144,7 @@ void TMoverParameters::ComputeTotalForce(double dt)
FTotal = FTrain - Sign(V) * FStand;
}
double TMoverParameters::BrakeForceR(double ratio, double velocity)
double TMoverParameters::BrakeForceR(double ratio, double velocity) const
{
double press = 0;
if (MBPM > 2)
@@ -10482,7 +10482,10 @@ void TMoverParameters::LoadFIZ_Brake(std::string const &line)
P2FTrans = 0;
}
CntrlPipePress = 5 + 0.001 * (Random(10) - Random(10)); // Ra 2014-07: trochę niedokładności
// Ra 2014-07: trochę niedokładności
// the draws are sequenced: the order of the operands of '-' is unspecified, and GCC and MSVC draw them the other way round
auto const firstdraw{Random(10)};
CntrlPipePress = 5 + 0.001 * (Random(10) - firstdraw);
extract_value(CntrlPipePress, "HiPP", line, "");
HighPipePress = CntrlPipePress;
LowPipePress = std::min(HighPipePress, 3.5);

View File

@@ -6,20 +6,28 @@
set(UNIT_TESTS_NAME eu07_unit_tests)
# a new test_*.cpp file or data/*.json definition is picked up on its own
# a new test_*.cpp file or file in data/ (definitions, real vehicles, golden traces) is picked up on its own
file(GLOB UNIT_TEST_CASES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/test_*.cpp")
file(GLOB UNIT_TEST_DATA CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/data/*.json")
file(GLOB_RECURSE UNIT_TEST_DATA CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/data/*")
add_executable(${UNIT_TESTS_NAME}
main.cpp
vehicle.cpp
fiz.cpp
drive.cpp
consist.cpp
scenario.cpp
golden.cpp
invariants.cpp
figures.cpp
engine_stubs.cpp
${UNIT_TEST_CASES})
# the core library brings the game's build settings along
target_link_libraries(${UNIT_TESTS_NAME} PRIVATE eu07_core)
target_include_directories(${UNIT_TESTS_NAME} PRIVATE "${DEPS_DIR}/doctest")
target_compile_definitions(${UNIT_TESTS_NAME} PRIVATE EU07_UNIT_TESTS_DATA="${CMAKE_CURRENT_SOURCE_DIR}/data")
# the names the tests CAPTURE are often C strings, shown as text rather than pointers
target_compile_definitions(${UNIT_TESTS_NAME} PRIVATE EU07_UNIT_TESTS_DATA="${CMAKE_CURRENT_SOURCE_DIR}/data" DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING)
if (MSVC)
target_compile_options(${UNIT_TESTS_NAME} PRIVATE /bigobj)
endif()

117
tests/unit/COVERAGE.md Normal file
View File

@@ -0,0 +1,117 @@
# Coverage of the simulation core by the unit tests
These tests characterise the physics of the vehicles (`McZapkie/Mover.cpp`, `hamulce.cpp`, `Oerlikon_ESt.cpp`, `friction.cpp`). They exist to show that refactors of those files, such as splitting `TractionForce` or `RunCommand`, leave the behaviour as it was. This page lists:
- what the tests reach;
- what nothing in the game ever runs;
- what only data no game vehicle has would run;
- what is left to test;
- the behaviour the tests found that may deserve a look.
## How it was measured
Coverage was measured on Windows with MSVC's SanitizerCoverage (`/fsanitize-coverage=trace-pc-guard`) on the unit test binary. The counts are basic blocks, which comes close to branch coverage: both sides of a condition are blocks of their own. The tool is local and not part of the build, by decision (2026-10). Any block-coverage tool run over `eu07_unit_tests` gives comparable numbers.
| File | Blocks run, at the start | Now |
|---|---|---|
| `Mover.cpp` | 8.9% | 80.7% (3323 of 4118) |
| `hamulce.cpp` | 30% | 76.3% (649 of 851) |
| `Oerlikon_ESt.cpp` | 58% | 87.4% (118 of 135) |
| `friction.cpp` | 9% | 90.9% (10 of 11) |
The aim is branch coverage with the rest explained below, not 100%.
## Code nothing in the game runs
Nothing creates these objects or calls these functions anywhere in the game, which was checked against the whole source tree. A refactor can delete them, or leave them untested.
- **Brake valves that never get created:**
- `TESt`, `TESt3` and `TESt3AL2`, including `Init`, `PLC` and `SetLP`. The valve names ESt3, ESt3AL2 and ESt4 all create `TNESt3`.
- `TESt4R::GetPF`, overridden by `TLSt` and never called by it.
- `TESt::CheckState`, `CVs`, `BVs`, `GetPF` and `EStParams`, and `TNESt3::EStParams`.
- **`TFV4a`** (`GetPF`, `Init`): the FV4a handle of the vehicle definitions creates `TFV4aM`.
- **Base implementations always overridden:** `TFricMat::GetFC` (every material, the default P10 too, has its own) and `TPrzekladnik::Update` (every relay has its own).
- **Functions without callers:**
- In `hamulce.cpp`: `PF_old`, `TBrake::GetBCF`, `TDriverHandle::OvrldButton`, `TReservoir::pa` and `TBrakeCyl::pa`.
- In `Mover.cpp`: `BrakeVP`, `PipeRatio`, `RealPipeRatio`, `IncBrakeLevel`, `DecBrakeLevel`, `IncBrakePress`, `DecBrakePress`, `MainCtrlActualPowerPos` and `GetExternalCommand`.
- **Inverter failures** of the induction motor drives (`inverter::Failure_Drive`, `Failure_Const`, `Activate`): nothing ever sets them. The cab only shows them. The branches reading them in `TractionForce` can't be reached.
## Code only data no game vehicle has would run
Where it was worth it, a test fits the missing parameter to a real vehicle or builds a definition with it (`definition::set()`). Those cases are marked "tested" below. The rest is reachable only by a vehicle definition the game data doesn't have:
- **Tested, fitted or handmade:**
- The valves CV1, CV1_R, CV1_L_TR, the W, K and Hik variants, SW, NESt3 and M483 (`brake cycle: the valves the game knows…`).
- An Oerlikon valve named with -ED or AL2 without ESt3 (its relays `TPrzekED` and `TPrzekCiagly`).
- The brake materials FR513, FR510, P10yBg and P10yBgu (`brake cycle: every material…`).
- The handles test, D2, Westinghouse, MHZ_EN57 and St113 fitted to an EU07.
- A `CompressorList`, a `PmaxList` and a `Headlights` section.
- The rear cab's compressor limits (`MinCP_B`, `MaxCP_B`).
- The controller of the electrodynamic brake apart from the local brake (`SplitEDPneumaticBrake`).
- **Not tested:**
- The retarder's clutch (`R_IsClutch`, `R_ClutchSpeed`) and the retarder worked by the local brake (`R_WithIndividual`).
- The automatic relays of type 2 (`RelayType=2`).
- The variant of `CheckLocomotiveParameters` for a definition whose `RList` doesn't match its powered axles.
## Reachable, not tested yet
These are run by the game, by vehicles it has or by the cab, the AI or the scenery, but no test reaches them yet. Most of them are the far sides of conditions inside functions the tests do run.
- **Entry points from outside the core:**
- `TBrake::ForceEmptiness` and its overrides: a scenario's vehicle set up with empty brakes (`DynObj.cpp`).
- `TBrake::GetSoundFlag`: the sounds of the brakes.
- `LoadFIZ` and `reload_FIZ`: the file loading around `LoadFIZ_Text`, which the tests call directly.
- `TSecuritySystem::set_cabsignal_lock`.
- `TMHZ_EN57::GetEP`: an MHZ_EN57 handle in the EP mode of the brake.
- **Inside the functions the tests run** (blocks run of the function's total):
- `TractionForce` (305 of 373): the inverter failures above. The rest are mostly halves of conditions:
- the `SST` table of the series-excited diesel-electrics;
- the speed control's brake intervention with the air under `BrakeInterventionVel`;
- a diesel-electric's generator tripping on overcurrent;
- the `ffBrakeList` of the 22WEc, whose electrodynamic brake no test applies.
- `AutoRelayCheck` (87 of 119): the ET22's group switch under some sequences, the relays at reverse with `BackwardsBranchesAllowed`, and the shunting of the EZTs.
- `update_doors` (134 of 160), `PantographsCheck` (44 of 64) and `PowerCouplersCheck` (70 of 88): door modes and pantograph valve kinds only some game vehicles have, and the 24 V circuit across couplers.
- `RelayReset` (29 of 43) and `MainSwitchCheck` (16 of 27): relays tripping in states the drive cycles don't reach.
- `UpdatePipePressure` (108 of 130): the pressure switch lock of an electric unit's pantographs, which the EW58's automatic valve keeps away. Also the EP mode of the EN57's handle and the `LockPipe` thresholds (`LPOn`, `LPOff`), which no vehicle sets.
- `TCV1L_TR::GetPF` (9 of 20), `TKE::GetPF` (28 of 37) and the MHZ handles: settings (G with the CV1L_TR, the KE's anti-slip pressure `ASBP`) that no real vehicle has together.
## What the tests found
The tests record the behaviour as it is. The following points may deserve a look by someone who knows how the real vehicles behave.
- **`Random(10) - Random(10)` (`Mover.cpp`, the noise of the control pipe pressure `CntrlPipePress`):** the order the two calls were evaluated in isn't specified. MSVC Release, MSVC Debug and GCC gave different traces, a control pipe pressure mirrored around 5 bar, which failed 32 tests on the Linux CI. Fixed: the draws are sequenced in the order of MSVC Release, which the golden traces were recorded with.
- **`TCommand::Location` isn't initialised** in a new vehicle. The game always sets it through `PutCommand()`, but a test calling `RunCommand()` directly got different results run to run.
- **`CompressorList` is `int[4][9]`** (`MOVER.h`), though its factors are fractions: 0.9 is read as 0. No game vehicle has the list yet.
- **A train whose pipe leaks under 2.75 bar before its start-up can't be released:**
- Where this happens: an FV4a handle and an LSt valve, for example 4 minutes standing with wagons, waiting for a hot engine.
- The cause: the pipe gets cut off the main reservoir (`lock_old` in `UpdatePipePressure`). The AI presses its unlock button only for the lock set by `LPOn` and `LPOff` (`control_main_pipe`).
- The same after every emergency stop of such a train, by the alarm chain or the radio-stop: reset, it stays with its pipe empty until the releaser is held (`b_rls`), which the test of the emergency stops does.
- **EW58:**
- Its electrodynamic brake comes in only by the stages of the ep brake (TUHEX), never by the brake pipe alone.
- It needs 110 V, from the converter of the middle car only.
- **ET42:**
- At the high current setting the controller stops at the last series position, and the train crawls.
- From the low setting's top speed (over 100 km/h) the dynamic brake's current trips the overload relay at once.
- **EU47:** its local brake sets the flag of the electrodynamic brake, which keeps the train brake's air out, but the motors don't brake. The local brake's air does it.
- **EP09:** the flag of the automatic electrodynamic brake stays on at a standstill while the cylinders hold less than 2.1 bar, and it flicks on while a high application is released.
- **Diesel engines with a second water circuit (SU45, ST44) started at 88 degrees:** they don't start until that circuit, over its limit (74 or 75 degrees), cools. Off, it cools slowly.
- **Brake cylinders over the vehicle's highest pressure** (found by the invariants, see the README):
- The K valve of the SM03 and the EU04 fills the cylinders to the pressure of the auxiliary reservoir in a full service or emergency application: 4.27 bar of the SM03's MaxBP 3.5, 3.19 bar of the EU04's 2.8.
- The ESt3d_PZZ valve of the SN61 does so in an emergency: 4.6 bar of its MaxBP 3.8.
- The EU07's LSt overshoots a little, to 4.0 bar of its 3.8.
- **Diesel engines driven over their top speed:** a lower gear taken at speed drives the engine through the wheels over its `nmax`, the SM03's by 10%, the WMB10's by 25%. The cutoff (`nmax_cutoff`) only takes the fuel off.
- **The ELF's C car's main reservoir over its governor:** the cars share the supply pipe, the A car's governor stops at 9 bar, the C car's at 8.5, so the C car's reservoir reaches 8.64 bar.
- **Wagons thrown about as a train starts with full physics of the couplers:** the slack taken up throws them at up to 5.7 g, and back within a step.
- **Nothing limits the speed:** a light train at full power runs well past its Vmax (the 754 past 160 km/h, against a Vmax of 100).
- **Vehicles that don't do what they should, as driven by the tests:**
- An EN57 doesn't get ready (main reservoir).
- An EN57AKL alone doesn't charge its pipe.
- The TEM2 barely moves, and the SM31 crawls at 0.3 km/h at full power, its motors taking 444 A.
- The draisine can't start from rest (the pump's force is under the resistance of a standing draisine).
- The Elf and the Impuls, their doors opened with the steps permitted, close the doors as they start off but keep the steps out, which keeps them from moving.
- The ED72 holds an ep application with its handle at running, and with the ep brake switched off too; only the ep release position lets it off. The EW58 releases at running.
- The road vehicle (MAN) is only slowed down by a minute of full service.
- **A train run with five physics steps in a frame:** an EU07 with wagons takes no current, its controller at full power, and rolls at a few km/h. The test steps the vehicles the way `TDynamicObject` does (`FastComputeMovement` for the steps before the last one), so the game may do the same.
- **Game data:**
- The 214M's file has `TurboPos` and `MotorParamTable` twice, and the EN57AL's `Engine` twice. The loader reads each in turn, so the later one overrides what both set.
- A wagon set up braked keeps its hand brake on until someone releases it. The AI does that for the whole train (`AutoRewident`), so a test has to as well.

View File

@@ -10,6 +10,9 @@ The code under test is `eu07_core`, the library of the simulation parts the game
game uses it. The python library gets linked along with the game's settings, but the tests don't
use it and run without it.
[COVERAGE.md](COVERAGE.md) says how much of the simulation core the tests reach, what's left and why, and
what they found.
## Running
- **With the build**: building the game (`WITH_TESTS`, on by default) runs them at the end, once the
@@ -46,24 +49,127 @@ TEST_CASE("brakes: service braking stops a heavier wagon too") {
- `definition::load("<name>")` reads `data/<name>.json`, with the sections and keys of the game's
vehicle definitions; `set()` and `remove()` change it for the test. Table sections give their
rows as `"rows": [[...], ...]`, keys starting with `_` are comments. A section the loader doesn't
know is an error (the table of them is in `vehicle.cpp`); a misspelt key isn't caught, so check
know is an error (the table of them is in `fiz.cpp`); a misspelt key isn't caught, so check
in the test that the value the test is about made it to the vehicle. Keep the definitions
physically sensible: some parts need keys others don't (the ESt brake valves need `Brake.Size`),
and a test on a half-defined vehicle tests the definition rather than the simulation.
- `definition::fiz("<dir>/<name>")` is one of the game's own vehicles, `data/fiz/<dir>/<name>.json`,
in the same form, converted from the game's `.fiz` with the files it includes (the list and how to
add one: `data/fiz/README.md`). The conversion keeps what the game's loader reads of the file and
checks it reads the same of the converted one (`fiz.h`), so the vehicle is the game's.
- The random numbers of the simulation are reseeded before every test case, so a test gives the
same result alone or among the others.
same result alone or among the others. Loading a vehicle draws some too (the scatter of its brake
pipe pressure), so a test making vehicles in a loop calls `test::reseed_random()` before each.
- Logic which lives in classes too big to set up in a test (the AI driver, the cab) goes into free
functions on the vehicle state the class calls, and the test calls them the same way: see
`vehicle/handover.h`, which `TController::TakeControl()` uses, and `test_handover.cpp`.
- `vehicle` creates the vehicle the way `TDynamicObject::Init()` does and steps it the way the
simulation loop does, on a straight track of the given friction and gradient. `pair` puts two of
them on that track facing each other with the given ends and runs them together, as the
simulation runs the vehicles of a scene: the coupler and buffer forces, collisions and automatic
coupling come from the physics (`test_couplers.cpp`). Longer consists aren't supported yet.
simulation loop does, on a straight track of the given friction, gradient, catenary voltage and
physics iterations a frame. With a cab (`vehicle{definition, 0.0, "name", 1}`) it has a driver.
- `consist` (`consist.h`) couples vehicles into a train and steps them together, coupler forces,
air, control and power passing between them. The physics reaches a neighbour through its
`TDynamicObject`, for its `MoverParameters` only; the tests have a stand-in of it with just that.
- `drive.h` has the driver's part, made of the AI's steps: `start_up()` (battery, pumps,
pantographs, line breaker, converter, compressor), `drive()` (running, acknowledging the
vigilance), `release_brakes()`, `drive_cycle()` (a whole run from start-up to a stop).
- `scenario` (`scenario.h`) puts a train, its driver and a golden trace for each vehicle together,
for a test of a feature: `act()` marks an event in the traces, `wait()` runs the train.
- `pair` (`vehicle.h`) puts two vehicles on that track facing each other with the given ends and
runs them together, as the simulation runs the vehicles of a scene: the coupler and buffer
forces, collisions and automatic coupling come from the physics (`test_couplers.cpp`).
- Check behaviour (speeds, pressures, states the player sees) rather than internal flags, and make
the test check its own preconditions (`REQUIRE` that the braking skidded, before checking what a
skid leads to).
## Golden traces
A golden trace pins down how the simulation behaves now, for changes which mustn't alter it, like
restructuring a function: the values of some channels of a vehicle over a run, compared with the
ones recorded from the code before, in `data/golden/<name>.csv`. A difference fails the test, naming
the channel, the time and both rows.
The samples and their key figures aren't kept in the repository (tens of megabytes, and they only mean
something against the code before the change): record them before the change, and compare after it.
```
git switch master
EU07_UNIT_TESTS_UPDATE_GOLDEN=1 eu07_unit_tests # records data/golden/**/*.csv, ignored by git
git switch my-refactor
EU07_UNIT_TESTS_GOLDEN_EXACT=1 eu07_unit_tests # compares every sample, bit for bit
```
The recorded files stay through switching branches. Without them, a trace checks the invariants and
its test's own `CHECK`s, which is what the CI does: every test states the behaviour it's about in
`CHECK`s, a scenario of a feature what the feature does (the doors open, the train stops), a run of
every real vehicle what holds for each of them (it brakes harder by every step, it runs off and stops).
```cpp
test::trace trace{"eu07/notch up", test::channels::motion() + test::channels::traction()};
eu07.record(trace); // samples every 10th step
eu07->MainSwitch(true);
trace.mark("main switch"); // shows in the event column of the row
eu07.run(30.0);
trace.check();
```
- `EU07_UNIT_TESTS_UPDATE_GOLDEN=1` records the samples and the key figures, for a new test or a
change which alters the behaviour on purpose; the diff of the key figures shows what it altered,
review it like code.
- Without `EU07_UNIT_TESTS_GOLDEN_EXACT=1`, samples recorded are compared within a relative 1e-9, as
GCC and glibc's maths functions can differ from MSVC's in the last bits, and a trace without them
passes. With it they're compared exactly, and a trace without them or its key figures fails, so a
run of the refactoring check can't pass by having nothing to compare.
- Keep the runs short (tens of seconds): the simulation feeds back on itself, and a last bit grows.
- A trace with no assertion about what the behaviour should be is a characterisation, bugs included;
put the behaviour the test is about into `CHECK`s besides.
### Key figures
Next to its samples, every trace records the figures a person reads (`figures.h`), for each part of
the run between two events, in `data/golden/<name>.figures`: the duration, the speeds, the distance,
the time to stop, the brake pipe's lowest pressure and the time to get back to 4.9 bar, the peaks of
the brake cylinders, the motor current and the tractive force.
```
[brake]
duration = 60.00 s
speed at the start = 118.5 km/h
distance = 944.3 m
stopped after = 45.97 s
brake cylinders peak = 3.997 bar
```
They're compared within 0.5% or the last decimal written, loose enough for any compiler, and the
differences are told by part and figure:
```
key figures of "traction/4e_v1/4e" differ:
[brake] distance: 944.3 m now, 980.0 m recorded
```
`EU07_UNIT_TESTS_UPDATE_GOLDEN=1` records them with the samples. After a change which alters the
behaviour, their comparison says in a few lines what the thousands of changed samples mean. A trace
sampled by hand, at times of its own (the commands and switches tests), records none.
### Invariants
Every trace also checks, on every step of its vehicle and not only on the recorded ones, the rules
which hold for any vehicle whatever a test does (`invariants.h`): no NaN, the brake cylinders under
the vehicle's highest pressure, the pipes under the highest governor setting, no motor current with
the line breaker open, no tractive force without power, the distance counted as the speed runs, no
acceleration over 5 g, and a few more. They compare nothing with a recorded file, so they hold on
any compiler, and they still check a behaviour recorded anew on purpose makes sense. A broken rule
fails the test, naming the time, the last event and the values:
```
invariant "no motor current with the line breaker open" broken in traction/et42 fuse/0 at 63.200 s,
after "dynamic brake": Mains=0 Im=-412.5 Itot=-412.5
```
Where a test makes a vehicle break a rule (a collision), or a game vehicle breaks one as it is, the
list of excuses in `invariants.cpp` names the trace, the rule and the reason; the second kind goes
into the findings of [COVERAGE.md](COVERAGE.md) too.
## Code to test, and when the tests fail to link
A source joins the tested code by moving from `SOURCES` to `CORE_SOURCES` in the main

106
tests/unit/consist.cpp Normal file
View File

@@ -0,0 +1,106 @@
#include "stdafx.h"
#include "consist.h"
#include "vehicle/DynObj.h"
namespace test {
consist::consist() = default;
consist::~consist() = default;
vehicle &consist::add(definition const &Definition, int Cab, int Coupling, double Velocity, double Gap) {
auto added{std::make_unique<vehicle>(Definition, Velocity, std::format("test{}", m_vehicles.size()), Cab)};
auto &mover{added->mover()};
std::vector<std::byte> object(sizeof(TDynamicObject));
reinterpret_cast<TDynamicObject *>(object.data())->MoverParameters = &mover; // NOSONAR see m_objects
auto position{0.0};
if (false == m_vehicles.empty()) {
auto &last{m_vehicles.back()->mover()};
position = m_positions.back() - 0.5 * (last.Dim.L + mover.Dim.L) - Gap;
mover.Loc = TLocation{position, 0.0, 0.0};
if (Coupling != coupling::faux) {
last.Attach(end::rear, end::front, &mover, Coupling, true, false);
mover.Attach(end::front, end::rear, &last, Coupling, true, false);
}
}
else {
mover.Loc = TLocation{position, 0.0, 0.0};
}
m_vehicles.push_back(std::move(added));
m_positions.push_back(position);
m_objects.push_back(std::move(object));
update_neighbours();
return *m_vehicles.back();
}
void consist::update_neighbours() {
// TDynamicObject::update_neighbours(), for vehicles coupled in a line
for (std::size_t i = 0; i < m_vehicles.size(); ++i) {
auto &mover{m_vehicles[i]->mover()};
for (int end = end::front; end <= end::rear; ++end) {
auto &neighbour{mover.Neighbours[end]};
auto const other{end == end::front ? static_cast<std::ptrdiff_t>(i) - 1 : static_cast<std::ptrdiff_t>(i) + 1};
if (other < 0 || other >= static_cast<std::ptrdiff_t>(m_vehicles.size())) {
neighbour = neighbour_data();
continue;
}
auto const &othermover{m_vehicles[other]->mover()};
auto const otherend{end == end::front ? end::rear : end::front};
auto const distance{TMoverParameters::CouplerDist(&mover, &othermover) - mover.Couplers[end].adapter_length -
othermover.Couplers[otherend].adapter_length};
// a vehicle not coupled is a collision source when it's within the range the game scans
if (mover.Couplers[end].Connected == nullptr && distance > std::max(10.0, std::abs(mover.V)) + 40.0) {
neighbour = neighbour_data();
continue;
}
neighbour.vehicle = reinterpret_cast<TDynamicObject *>(m_objects[other].data()); // NOSONAR see m_objects
neighbour.vehicle_end = otherend;
neighbour.distance = static_cast<float>(distance);
}
}
}
void consist::step(track const &Track, double Dt) {
for (auto const &vehicle : m_vehicles) {
vehicle->begin_step();
}
update_neighbours();
for (int iteration = 1; iteration < Track.iterations; ++iteration) {
for (auto const &vehicle : m_vehicles) {
vehicle->forces(Dt);
}
for (std::size_t i = 0; i < m_vehicles.size(); ++i) {
m_positions[i] += m_vehicles[i]->fast_move(Track, Dt, TLocation{m_positions[i], 0.0, 0.0});
}
}
for (auto const &vehicle : m_vehicles) {
vehicle->forces(Dt);
}
// the average pressure in the train's brake pipe weighed by the vehicles' lengths, TDynamicObject::GetEPP(),
// over the whole train (the game's stops at a vehicle without the brake hose coupled)
auto pipe{0.0};
auto length{0.0};
for (auto const &vehicle : m_vehicles) {
pipe += vehicle->mover().PipePress * vehicle->mover().Dim.L;
length += vehicle->mover().Dim.L;
}
pipe /= length;
for (std::size_t i = 0; i < m_vehicles.size(); ++i) {
m_positions[i] += m_vehicles[i]->move(Track, Dt, TLocation{m_positions[i], 0.0, 0.0}, pipe);
}
}
void consist::run(double Seconds, track const &Track, double Dt) {
auto const steps{std::lround(Seconds / (Dt * Track.iterations))};
for (long i = 0; i < steps; ++i) {
step(Track, Dt);
}
}
double consist::gap(std::size_t Index) const {
return TMoverParameters::CouplerDist(&m_vehicles[Index]->mover(), &m_vehicles[Index + 1]->mover());
}
} // namespace test

57
tests/unit/consist.h Normal file
View File

@@ -0,0 +1,57 @@
#pragma once
#include <cstddef>
#include <memory>
#include <vector>
#include "vehicle.h"
class TDynamicObject;
namespace test {
// vehicles coupled into a train on a straight track, all facing forward, from the front one back, stepped the
// way vehicle_table::update() steps the vehicles of the game: the constants and neighbours of all of them,
// their forces, then their movement. couplers carry the forces, the air, the control and the power between
// the vehicles as in the game:
// test::consist en57;
// en57.add(definition::fiz("en57_v1/5bgb"), 1); // driven from the front cab
// en57.add(definition::fiz("en57_v1/6ba_840"));
// en57.add(definition::fiz("en57_v1/5bgb"));
class consist {
public:
// everything a coupler can join, as two vehicles of a train coupled by a crew
static constexpr int full_coupling{coupling::coupler | coupling::brakehose | coupling::mainhose | coupling::control | coupling::gangway | coupling::heating |
coupling::permanent | coupling::power110v | coupling::power24v | coupling::highvoltage};
consist();
~consist();
consist(consist const &) = delete;
consist &operator=(consist const &) = delete;
// a vehicle at the back of the train, its front coupler joined to the rear one of the last vehicle with
// the given coupling (coupling flags), Gap [m] between their buffers. Cab as for a vehicle. a vehicle
// with no coupling (coupling::faux) is free on the same track, as one about to be coupled or hit
vehicle &add(definition const &Definition, int Cab = 0, int Coupling = full_coupling, double Velocity = 0.0, double Gap = 0.0);
vehicle &operator[](std::size_t Index) { return *m_vehicles[Index]; }
std::size_t size() const { return m_vehicles.size(); }
void step(track const &Track = {}, double Dt = 0.01);
void run(double Seconds, track const &Track = {}, double Dt = 0.01);
// distance between the couplers of the vehicle and the one behind it [m], negative when the buffers press
double gap(std::size_t Index) const;
private:
void update_neighbours();
std::vector<std::unique_ptr<vehicle>> m_vehicles;
std::vector<double> m_positions; // of the vehicles' middles along the track
// the physics reaches a neighbouring vehicle through its TDynamicObject, for its MoverParameters alone.
// the game's object can't be made without the rest of the game, so it's a stand-in: storage of its size
// with that one member set, never constructed, which only the physics ever looks into
std::vector<std::vector<std::byte>> m_objects;
};
} // namespace test

View File

@@ -0,0 +1,14 @@
{
"_source": "pkp/11xa_v2/93a.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "40000", "Vmax": "120", "HeatingP": "32", "LightP": "0.56"},
"Load": {"MaxLoad": "8", "LoadQ": "pieces", "LoadAccepted": "Workers", "OverLoadFactor": "1.5", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "24.5", "H": "4", "W": "2.9", "Cx": "0.1"},
"Wheels": {"D": "0.92", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.50", "Bd": "17.2", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "ESt4", "Size": "200", "NBpA": "4", "MBF": "90", "MaxBP": "4.0", "BCN": "2", "BCR": "0.1778", "BCD": "0.150", "BCM": "8.17", "BCS": "1.4", "BSA": "4.0", "BM": "P10-Bgu", "BVV": "200", "BRE": "0.87"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.11", "FmaxB": "320", "kC": "4500", "DmaxC": "0.1", "FmaxC": "850", "beta": "0.58", "AllowedFlag": "115"},
"Cntrl": {"BrakeSystem": "Pneumatic", "LocalBrake": "ManualBrake", "BrakeDelays": "R", "ASB": "Yes", "MCPN": "1", "rows": []},
"Light": {"Light": "Generator", "LGeneratorEngine": "WheelsDriven", "LMaxVoltage": "24", "AlterLight": "Accu", "AlterLMaxVoltage": "24", "AlterLCap": "495", "AlterLRS": "Generator"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "3000"},
"Doors": {"OpenCtrl": "Passenger", "CloseCtrl": "Mixed", "OpenSpeed": "70", "CloseSpeed": "400", "DoorMaxShiftL": "80", "DoorMaxShiftR": "80", "DoorOpenMethod": "Fold", "DoorClosureWarning": "No"}
}

View File

@@ -0,0 +1,14 @@
{
"_source": "pkp/14xa_v1/173a.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "42000", "Mred": "1600", "Vmax": "160", "HeatingP": "42", "LightP": "0.56"},
"Load": {"MaxLoad": "30", "LoadQ": "pieces", "LoadAccepted": "Passengers", "OverLoadFactor": "1.5", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "24.5", "H": "4", "W": "2.9", "Cx": "0.1"},
"Wheels": {"D": "0.92", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.70", "Bd": "17.2", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "ESt4HBG300-s216", "Size": "375", "NBpA": "4", "MBF": "90", "MaxBP": "3.8", "BCN": "2", "BCR": "0.2032", "BCD": "0.150", "BCM": "9.04", "BCS": "1.6", "BSA": "4.0", "BM": "P10-Bgu", "BVV": "375", "BRE": "0.95"},
"Doors": {"OpenCtrl": "Passenger", "CloseCtrl": "AutomaticCtrl", "OpenSpeed": "0.36", "CloseSpeed": "0.48", "DoorCloseDelay": "2.2", "DoorMaxShiftL": "0.9", "DoorMaxShiftR": "0.9", "DoorOpenMethod": "Plug", "DoorClosureWarning": "yes", "DoorBlocked": "Yes", "DoorMaxShiftPlug": "0.043", "PlatformMaxShift": "-45", "PlatformSpeed": "4", "PlatformOpenMethod": "Rotate", "DoorStayOpen": "10"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.11", "FmaxB": "320", "kC": "4500", "DmaxC": "0.1", "FmaxC": "850", "beta": "0.58", "AllowedFlag": "115"},
"Cntrl": {"BrakeSystem": "Pneumatic", "LocalBrake": "ManualBrake", "BrakeDelays": "R", "ASB": "Yes", "MCPN": "1", "rows": []},
"Light": {"Light": "Generator", "LGeneratorEngine": "WheelsDriven", "LMaxVoltage": "24", "AlterLight": "Accu", "AlterLMaxVoltage": "24", "AlterLCap": "495", "AlterLRS": "Generator"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "3000"}
}

View File

@@ -0,0 +1,14 @@
{
"_source": "pkp/168a_v1/168a.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "42000", "Mred": "1600", "Vmax": "160", "HeatingP": "20", "LightP": "0.56"},
"Load": {"MaxLoad": "56", "LoadQ": "pieces", "LoadAccepted": "Passengers,Luggage", "OverLoadFactor": "1.5", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "24.5", "H": "4", "W": "2.9", "Cx": "0.1"},
"Wheels": {"D": "0.92", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.70", "Bd": "17.2", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "ESt4HBG300", "Size": "200", "NBpA": "4", "MBF": "90", "MaxBP": "3.8", "BCN": "2", "BCR": "0.1778", "BCD": "0.150", "BCM": "10.00", "BCS": "1.4", "BSA": "6.0", "BM": "P10-Bgu", "BVV": "200", "BRE": "0.9"},
"Doors": {"OpenCtrl": "Passenger", "CloseCtrl": "AutomaticCtrl", "OpenSpeed": "0.44", "CloseSpeed": "0.27", "DoorCloseDelay": "2.25", "DoorMaxShiftL": "0.9", "DoorMaxShiftR": "0.9", "DoorOpenMethod": "Plug", "DoorClosureWarning": "yes", "DoorBlocked": "Yes", "DoorMaxShiftPlug": "0.043", "PlatformMaxShift": "-45", "PlatformSpeed": "4", "PlatformOpenMethod": "Rotate", "DoorStayOpen": "10"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.11", "FmaxB": "320", "kC": "4500", "DmaxC": "0.1", "FmaxC": "850", "beta": "0.58", "AllowedFlag": "115"},
"Cntrl": {"BrakeSystem": "Pneumatic", "LocalBrake": "ManualBrake", "BrakeDelays": "R", "ASB": "Yes", "MCPN": "1", "rows": []},
"Light": {"Light": "Generator", "LGeneratorEngine": "WheelsDriven", "LMaxVoltage": "24", "AlterLight": "Accu", "AlterLMaxVoltage": "24", "AlterLCap": "495", "AlterLRS": "Generator"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "3000"}
}

View File

@@ -0,0 +1,78 @@
{
"_source": "pkp/21xm_v1/214ma.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "46000", "Vmax": "120", "PWR": "390", "SandCap": "100", "Type": "dmu"},
"Load": {"MaxLoad": "50", "OverLoadFactor": "2.2", "LoadQ": "pieces", "LoadAccepted": "passengers", "LoadSpeed": "0.5", "UnLoadSpeed": "0.7"},
"Dimensions": {"L": "24.38", "H": "4.2", "W": "2.9", "Cx": "0.0"},
"Wheels": {"D": "0.84", "Tw": "1.435", "AIM": "300", "Axle": "B'2'", "Ad": "2", "Bd": "17.23", "Rmin": "75", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EStED", "Size": "150", "NBpA": "4", "MBF": "2.74", "MaxBP": "2.9", "LocMaxBP": "3.8", "RV": "-1", "RM": "0.77", "BCN": "8", "BCR": "0.127", "BCD": "0.1", "BCM": "2.15", "HiPP": "5", "LoPP": "3.5", "Vv": "0.125", "MinCP": "7.5", "MaxCP": "8.5", "CompressorSpeed": "0.0138", "CompressorPower": "Main", "BCS": "0.5", "BSA": "0", "BM": "Cosid", "BVV": "88", "BRE": "0.97", "TareMaxBP": "2.2", "LPOn": "3.0", "LPOff": "4.5", "UBB1": "2", "UBB2": "8"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.25", "CloseSpeed": "0.19", "DoorCloseDelay": "0.1", "DoorMaxShiftL": "0.78", "DoorMaxShiftR": "0.78", "DoorOpenMethod": "Plug", "DoorClosureWarning": "No", "DoorBlocked": "Yes", "DoorMaxShiftPlug": "0.04", "MirrorMaxShift": "75", "DoorStayOpen": "10", "DoorClosexDelay": "1.8", "DoorNeedPermit": "Yes", "DoorPermitList": "1|0|2|3", "DoorPermitListDefault": "2", "DoorAutoCloseRemote": "No"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "ControlType": "214MA"},
"SpringBrake": {"Volume": "20", "MBF": "5.8", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "0.5", "ValveOnArea": "0.0005", "ValveOffArea": "0.0005", "ValvePNBArea": "0.002", "MTC": "4"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "3", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Yes", "LocalBrake": "PneumaticBrake", "ManualBrake": "No", "MCPN": "5", "SCPN": "5", "AutoRelay": "Yes", "IniCDelay": "1.0", "SCDelay": "0.3", "BrakeDelays": "PR", "MaxBPMass": "55", "BrakeHandle": "MHZ_K5P", "LocBrakeHandle": "FD1", "WaterStart": "Battery", "OilStart": "Automatic", "FuelStart": "Automatic", "ConverterStart": "Automatic", "ConverterStartDelay": "4", "BrakeOpModes": "PN", "EIMCtrlType": "3", "DirChangeMaxPos": "4", "SBD": "0.47", "AutomaticCabActivation": "No", "InactiveCabFlag": "27", "CompartmentLightsStart": "Manual", "rows": [
["-1","0.6","-1","20","Pneumatic"],
["0","0.5","-1","2","Pneumatic"],
["1","0.46","-1","2","Pneumatic"],
["2","0.42","-1","2","Pneumatic"],
["3","0.38","-1","2","Pneumatic"],
["4","0.34","-1","2","Pneumatic"],
["5","0.29","-1","2","Pneumatic"],
["6","0","-1","40","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "2.4", "EmergencyBrakeDelay": "2.1", "RadioStop": "Yes", "CabDependent": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "28", "LMaxCurrent": "500"},
"Clima": {"HMaxVoltage": "3000", "Heating": "Generator", "HGeneratorEngine": "Main", "HGeneratorMinRPM": "696", "HGeneratorMinVoltage": "2400", "HGeneratorMaxRPM": "1500", "HGeneratorMaxVoltage": "3300"},
"Engine": {"EngineType": "DieselEngine", "Trans": "50:129", "TransEff": "0.91", "nmin": "800", "nmax": "2000", "nmax_cutoff": "1950", "AIM": "65", "EUS": "102.5", "EDS": "102.0", "IsTC": "Yes", "TC_TMMax": "1.82", "TC_CP": "0.75", "TC_LT": "3000", "TC_LR": "1", "TC_ULR": "1", "TC_FRI": "1", "TC_FRD": "2", "TC_TII": "2.93", "TC_TIO": "0", "TC_TOO": "-1.65", "TC_LS": "29.5", "TC_ULS": "40", "IsRetarder": "Yes", "R_Place": "1", "R_TII": "5.0", "R_MT": "1500", "R_MP": "250", "R_FRI": "1", "R_FRD": "1", "R_MinVel": "30", "WaterMaxTemperature": "105", "WaterCoolingTemperature": "90", "WaterFlowTemperature": "35", "WaterAuxCircuit": "Yes"},
"SpeedControl": {"SpeedCtrl": "Yes", "SpeedButtons": "0|30|40|50|60|70|80|90|100|120", "OverrideManual": "Yes", "InitPwr": "0.3", "MaxPwrVel": "5", "StartVel": "2", "VelStep": "5", "PwrStep": "0.1", "MinPwr": "0.3", "MaxPwr": "1.0", "MinVel": "0", "MaxVel": "120", "Offset": "-0.5", "kPpos": "0.1", "kPneg": "0.4", "kIpos": "0.06", "kIneg": "0.06", "BrakeIntervention": "Yes", "PowerUpSpeed": "0.3", "PowerDownSpeed": "0.3"},
"TurboPos": {"TurboPos": "1"},
"MotorParamTable": {"minVelfullengage": "3.0", "engageDia": "1", "engageMaxForce": "3000", "engagefriction": "1", "rows": []},
"MotorParamTable0": {"minVelfullengage": "3.0", "engageDia": "1", "engageMaxForce": "3000", "engagefriction": "1", "rows": [
["0","0","0","0","-1","-1","0","1"],
["1","2.81","-1","-1","38.2","42.4","0.18","1"],
["2","1.84","17.4","29.9","53.8","59.8","0.13","1"],
["3","1.36","26.6","45.0","70.8","78.7","0","1"],
["4","1.00","61.1","61.1","90.4","100.4","0","1"],
["5","0.80","80.2","80.2","200","200","0","1"]
]},
"DList": {"Size": "5", "Mmax": "2450", "nMmax": "21.7", "nmax": "33", "Mnmax": "2160", "nominalfill": "1.0", "Mstand": "250.0", "NomFuelConsRate": "220", "rows": [
["0","0.15","0"],
["1","0.20","2"],
["2","0.40","2"],
["3","0.60","2"],
["4","0.80","2"],
["5","1.00","2"]
]},
"DMList": {"rows": [
["0","0"],
["80","637"],
["400","796"],
["800","1592"],
["1050","2090"],
["1300","2200"],
["1550","2124"],
["1800","2070"],
["2000","1676"]
]},
"UCList": {"Size": "5", "IntegratedBrakePN": "Yes", "IntegratedBrake": "Yes", "rows": [
["0","3","0","0","0","0.15","0.15","0","1","0"],
["1","2","0","0","0","0.15","0.15","1","3","0"],
["2","1","-1","0","-1","0.15","0.15","3","3","1"],
["3","1","-1","1","0","0.00","0.15","3","4","1"],
["4","0","-1","1","0","0.15","0.00","4","5","3"],
["5","0","0","1","1","0.15","0.15","4","5","3"]
]},
"LightsList": {"Size": "5", "Wrap": "no", "Default": "1", "rows": [
["0","0"],
["21","34"],
["22","34"],
["34","34"],
["16","1"]
]},
"WiperList": {"Size": "5", "Angle": "75", "Default": "1", "rows": [
["3","0.5","0.5","0.0"],
["0","0.8","0.0","0.5"],
["3","0.8","6.0","0.5"],
["3","0.8","3.0","0.0"],
["3","0.8","1.0","0.0"]
]}
}

View File

@@ -0,0 +1,84 @@
{
"_source": "pkp/21xm_v1/218ma_a_kd.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "38500", "Vmax": "120", "PWR": "360", "SandCap": "100", "Type": "dmu"},
"Load": {"MaxLoad": "50", "OverLoadFactor": "2.2", "LoadQ": "pieces", "LoadAccepted": "passengers", "LoadSpeed": "0.5", "UnLoadSpeed": "0.7"},
"Dimensions": {"L": "20.75", "H": "4.2", "W": "2.9", "Cx": "0.10"},
"Wheels": {"D": "0.84", "Tw": "1.435", "AIM": "300", "Axle": "B'1'", "Ad": "2", "Bd": "17.23", "Rmin": "75", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EStED", "Size": "150", "NBpA": "4", "MBF": "2.74", "MaxBP": "2.9", "LocMaxBP": "3.8", "RV": "-1", "RM": "0.77", "BCN": "6", "BCR": "0.127", "BCD": "0.1", "BCM": "2.15", "HiPP": "5", "LoPP": "3.5", "Vv": "0.125", "MinCP": "7.0", "MaxCP": "8.0", "CompressorSpeed": "0.015", "CompressorPower": "Main", "BCS": "0.5", "BSA": "0", "BM": "Cosid", "BVV": "88", "BRE": "0.97", "TareMaxBP": "2.2", "LPOn": "3.0", "LPOff": "4.5", "UBB1": "2", "UBB2": "8"},
"SpringBrake": {"Volume": "15", "MBF": "4.35", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "0.5", "ValveOnArea": "0.0005", "ValveOffArea": "0.0005", "ValvePNBArea": "0.002", "MTC": "4"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.25", "CloseSpeed": "0.19", "DoorCloseDelay": "0.1", "DoorMaxShiftL": "0.78", "DoorMaxShiftR": "0.78", "DoorOpenMethod": "Plug", "DoorClosureWarning": "No", "DoorBlocked": "Yes", "DoorMaxShiftPlug": "0.04", "MirrorMaxShift": "75", "DoorStayOpen": "10", "DoorNeedPermit": "Yes", "DoorPermitList": "1|0|2|3", "DoorPermitListDefault": "2", "DoorAutoCloseRemote": "No"},
"BuffCoupl1": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39"},
"BuffCoupl2": {"CType": "Articulated", "AllowedFlag": "-55"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "3", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Yes", "LocalBrake": "PneumaticBrake", "ManualBrake": "No", "MCPN": "5", "SCPN": "5", "AutoRelay": "Yes", "IniCDelay": "1.0", "SCDelay": "0.3", "BrakeDelays": "PR", "MaxBPMass": "55", "BrakeHandle": "MHZ_K5P", "LocBrakeHandle": "FD1", "WaterStart": "Battery", "OilStart": "Automatic", "FuelStart": "Automatic", "ConverterStart": "Automatic", "ConverterStartDelay": "4", "BrakeOpModes": "PN", "EIMCtrlType": "3", "DirChangeMaxPos": "4", "SBD": "0.47", "AutomaticCabActivation": "No", "InactiveCabFlag": "27", "CompartmentLightsStart": "Manual", "rows": [
["-1","0.6","-1","20","Pneumatic"],
["0","0.5","-1","2","Pneumatic"],
["1","0.46","-1","2","Pneumatic"],
["2","0.42","-1","2","Pneumatic"],
["3","0.38","-1","2","Pneumatic"],
["4","0.34","-1","2","Pneumatic"],
["5","0.29","-1","2","Pneumatic"],
["6","0","-1","40","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "2.4", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "2.1", "RadioStop": "Yes", "CabDependent": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "28", "LMaxCurrent": "500"},
"Engine": {"EngineType": "DieselEngine", "Trans": "50:129", "TransEff": "0.95", "nmin": "800", "nmax": "1800", "nmax_cutoff": "1850", "nreg_acc": "150", "AIM": "30", "EUS": "102.5", "EDS": "102.0", "IsTC": "Yes", "TC_TMMax": "1.82", "TC_CP": "0.85", "TC_LT": "2500", "TC_LR": "1", "TC_ULR": "1", "TC_FRI": "1", "TC_FRD": "2", "TC_TII": "2.93", "TC_TIO": "0", "TC_TOO": "-1.65", "TC_LS": "22", "TC_ULS": "40", "IsRetarder": "Yes", "R_Place": "1", "R_TII": "5.0", "R_MT": "1500", "R_MP": "250", "R_FRI": "1", "R_FRD": "1", "R_MinVel": "1", "WaterMaxTemperature": "105", "WaterCoolingTemperature": "95", "WaterFlowTemperature": "75", "WaterAuxCircuit": "No", "WaterCoolingFanSpeed": "-100", "NominalCoolingPower": "300"},
"Clima": {"HMaxVoltage": "3000", "Heating": "Generator", "HGeneratorEngine": "Main", "HGeneratorMinRPM": "696", "HGeneratorMinVoltage": "2400", "HGeneratorMaxRPM": "1500", "HGeneratorMaxVoltage": "3300"},
"SpeedControl": {"SpeedCtrl": "Yes", "SpeedButtons": "0|30|40|50|60|70|80|90|100|120", "OverrideManual": "Yes", "InitPwr": "0.3", "MaxPwrVel": "5", "StartVel": "2", "VelStep": "5", "PwrStep": "0.1", "MinPwr": "0.3", "MaxPwr": "1.0", "MinVel": "0", "MaxVel": "120", "Offset": "-0.5", "kPpos": "0.1", "kPneg": "0.4", "kIpos": "0.06", "kIneg": "0.06", "BrakeIntervention": "Yes", "PowerUpSpeed": "0.17", "PowerDownSpeed": "0.17"},
"TurboPos": {"TurboPos": "1"},
"MotorParamTable": {"minVelfullengage": "3.0", "engageDia": "1", "engageMaxForce": "2300", "engagefriction": "1", "rows": []},
"MotorParamTable0": {"minVelfullengage": "3.0", "engageDia": "1", "engageMaxForce": "2300", "engagefriction": "1", "rows": [
["0","0","0","0","-1","-1","0","1"],
["1","2.81","-1","-1","31.3","33.7","0.17","1"],
["2","1.84","17.4","29.9","47.9","51.5","0.11","1"],
["3","1.36","26.6","45.0","64.8","69.7","0","1"],
["4","1.00","61.1","61.1","84.2","90.8","0","1"],
["5","0.80","80.2","80.2","200","200","0","1"]
]},
"DList": {"Size": "5", "Mmax": "2450", "nMmax": "21.7", "nmax": "30", "Mnmax": "2160", "nominalfill": "1.0", "Mstand": "250.0", "NomFuelConsRate": "220", "rows": [
["0","0.15","0"],
["1","0.20","2"],
["2","0.40","2"],
["3","0.60","2"],
["4","0.80","2"],
["5","1.00","2"]
]},
"DMList": {"rows": [
["0","0"],
["80","637"],
["400","796"],
["800","1592"],
["1050","2090"],
["1300","2200"],
["1550","2032"],
["1800","1910"],
["2000","1547"]
]},
"V2NList": {"rows": [
["0","1200"],
["40","1800"],
["120","1800"]
]},
"UCList": {"Size": "5", "IntegratedBrakePN": "Yes", "IntegratedBrake": "Yes", "rows": [
["0","3","0","0","0","0.15","0.15","0","1","0"],
["1","2","0","0","0","0.15","0.15","1","3","0"],
["2","1","-1","0","-1","0.15","0.15","3","3","1"],
["3","1","-1","1","0","0.00","0.15","3","4","1"],
["4","0","-1","1","0","0.15","0.00","4","5","3"],
["5","0","0","1","1","0.15","0.15","4","5","3"]
]},
"LightsList": {"Size": "5", "Wrap": "no", "Default": "1", "rows": [
["0","0"],
["21","34"],
["22","34"],
["34","34"],
["16","1"]
]},
"WiperList": {"Size": "5", "Angle": "75", "Default": "1", "rows": [
["3","0.5","0.5","0.0"],
["0","0.8","0.0","0.5"],
["3","0.8","6.0","0.5"],
["3","0.8","3.0","0.0"],
["3","0.8","1.0","0.0"]
]}
}

View File

@@ -0,0 +1,11 @@
{
"_source": "pkp/3wl_v2/3wl.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "10500", "Vmax": "100"},
"Load": {"MaxLoad": "30", "LoadQ": "tonns", "LoadAccepted": "Coal,Sand,sand2,ruda,tluczen,scrap,scrap2", "LoadSpeed": "1", "UnLoadSpeed": "0.1"},
"Dimensions": {"L": "9.87", "H": "3", "W": "3", "Cx": "0.1"},
"Wheels": {"D": "1", "Tw": "1.435", "AIM": "300", "Axle": "1'1'", "Bd": "6", "Rmin": "30", "BearingType": "Roll"},
"Brake": {"BrakeValve": "ESt3", "Size": "12", "MBF": "72", "MaxBP": "3.8", "BCN": "1", "BCR": "0.1524", "BCD": "0.14", "BCMlo": "3.05", "BCMHi": "10.67", "NBpA": "4", "BM": "P10-Bg", "BCS": "2.7", "BSA": "8.00", "BVV": "50", "BRE": "0.8"},
"BuffCoupl": {"CType": "Screw", "kB": "9000", "DmaxB": "0.12", "FmaxB": "350", "kC": "4500", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.52", "AllowedFlag": "3"},
"Cntrl": {"BrakeSystem": "Pneumatic", "LocalBrake": "ManualBrake", "BrakeDelays": "GP", "rows": []}
}

View File

@@ -0,0 +1,11 @@
{
"_source": "pkp/430w_v2/430w.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "20500", "Vmax": "100"},
"Load": {"MaxLoad": "59.5", "LoadQ": "tonns", "LoadAccepted": "coal,piasek,piasek2,ruda,tluczen,zlom,zlom2,siarka", "LoadSpeed": "1", "UnLoadSpeed": "0.1"},
"Dimensions": {"L": "14.028", "H": "3.235", "W": "3.04", "Cx": "0.1"},
"Wheels": {"D": "0.92", "Tw": "1.435", "AIM": "300", "Axle": "2'2'", "Ad": "2", "Bd": "8.5", "Rmin": "75", "BearingType": "Roll"},
"Brake": {"BrakeValve": "ESt3AL2HBG300", "Size": "100", "MBF": "144", "MaxBP": "3.8", "TareMaxBP": "1.3", "BCN": "1", "BCR": "0.2032", "BCD": "0.145", "BCM": "12.00", "NBpA": "4", "BM": "P10-Bg", "BCS": "2.7", "BSA": "16.00", "BVV": "100", "BRE": "0.9"},
"BuffCoupl": {"CType": "Screw", "kB": "9000", "DmaxB": "0.12", "FmaxB": "350", "kC": "4500", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.52", "AllowedFlag": "3"},
"Cntrl": {"BrakeSystem": "Pneumatic", "LocalBrake": "ManualBrake", "BrakeDelays": "GP", "rows": []}
}

View File

@@ -0,0 +1,81 @@
{
"_source": "pkp/4e_v1/4e.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "80000", "Mred": "16000", "Vmax": "125", "PWR": "2000", "SandCap": "500"},
"Load": {"MaxLoad": "52", "LoadQ": "pieces", "LoadAccepted": "pantstate", "LoadSpeed": "1", "UnLoadSpeed": "0.1"},
"Dimensions": {"L": "15.915", "H": "4.348", "W": "2.903", "Cx": "0.38"},
"Wheels": {"D": "1.250", "AIM": "2000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "3.05", "Bd": "8.55", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.0", "MaxASBP": "1.1", "RM": "0.635", "BCN": "8", "BCR": "0.1143", "BCD": "0.05", "BCM": "5.18", "Vv": "0.828", "MinCP": "7.0", "MaxCP": "8.7", "BCS": "1", "BSA": "0.5", "BM": "P10-Bgu", "BVV": "278", "CompressorSpeed": "0.0566", "BRE": "0.9"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "103", "ControlType": "4e"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "7", "BDelay3": "39", "BDelay4": "17", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "DynamicBrake": "Passive", "MCPN": "43", "SCPN": "6", "AutoRelay": "No", "IniCDelay": "0.5", "SCDelay": "0.26", "SCDDelay": "0.05", "BrakeDelays": "GPR", "BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "MotorBlowersStart": "Converter", "rows": [
["-1","0.6","-1","12","Pneumatic"],
["0","0.5","-1","9","Pneumatic"],
["1","0.43","-1","9","Pneumatic"],
["2","0.40","-1","9","Pneumatic"],
["3","0.37","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.28","-1","9","Pneumatic"],
["6","0","-1","18","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareMinSpeed": "10", "AwareDelay": "60", "SoundSignalDelay": "3", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "4", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "2", "MaxVoltage": "3600", "MaxCurrent": "1250", "MinH": "0.48", "MaxH": "2.5", "CSW": "1.4", "MinV": "1900", "MinPress": "3.5", "MaxPress": "5.0", "PhysicalLayout": "3"},
"Engine": {"EngineType": "ElectricSeriesMotor", "Volt": "1500", "Trans": "18:79", "WindingRes": "0.14", "nmax": "2390", "MotorBlowersSpeed": "-1310"},
"MotorParamTable0": {"rows": [
["0","21.8","105","0.11","140.9","105","0.11"],
["1","20.22","140","0.07","130.7","140","0.07"],
["2","21.06","190","0.09","136.2","190","0.09"],
["3","23.95","160","0.25","154.8","160","0.25"],
["4","21.73","255","0.16","140.5","255","0.16"],
["5","24.66","170","0.35","159.4","170","0.35"],
["6","23","800","0.02","148.7","800","0.02"]
]},
"Circuit": {"CircuitRes": "0.5", "ImaxLo": "600", "ImaxHi": "750", "IminLo": "350", "IminHi": "470"},
"RList": {"Size": "43", "RVent": "Automatic", "RVentnmax": "3300", "RVentCutOff": "0.2", "rows": [
["0","0","0","0","0"],
["1","24.891","1","4","0"],
["2","16.821","1","4","0"],
["3","12.301","1","4","0"],
["4","9.771","1","4","0"],
["5","8.111","1","4","0"],
["6","6.915","1","4","0"],
["7","6.481","1","4","0"],
["8","6.047","1","4","0"],
["9","5.629","1","4","0"],
["10","5.211","1","4","0"],
["11","4.827","1","4","0"],
["12","4.443","1","4","0"],
["13","4.099","1","4","0"],
["14","3.755","1","4","0"],
["15","3.442","1","4","0"],
["16","3.129","1","4","0"],
["17","2.838","1","4","0"],
["18","2.547","1","4","0"],
["19","2.286","1","4","0"],
["20","2.025","1","4","0"],
["21","1.780","1","4","0"],
["22","1.535","1","4","0"],
["23","1.275","1","4","0"],
["24","1.095","1","4","0"],
["25","0.775","1","4","0"],
["26","0.525","1","4","0"],
["27","0.275","1","4","0"],
["28","0.000","1","4","0"],
["29","5.44","2","2","0"],
["30","4.41","2","2","0"],
["31","3.85","2","2","0"],
["32","3.29","2","2","0"],
["33","2.82","2","2","0"],
["34","2.36","2","2","0"],
["35","1.93","2","2","0"],
["36","1.56","2","2","0"],
["37","1.21","2","2","0"],
["38","0.89","2","2","0"],
["39","0.61","2","2","0"],
["40","0.34","2","2","0"],
["41","0.21","2","2","0"],
["42","0.10","2","2","0"],
["43","0.00","2","2","0"]
]}
}

View File

@@ -0,0 +1,66 @@
# Real vehicle definitions
The game's own vehicles (`dynamic/` of the MaSzyna release used for the tests, 2026-10), to test the simulation on the vehicles players drive. Chosen to cover every engine type, brake valve, brake handle, local brake, coupler type, door type and brake delay set the game data uses. `pkp/` is left out of the paths, the other operators keep theirs.
They aren't the game's files: each `.json` here holds what the game's vehicle loader reads of the `.fiz`, with the files it includes, in the form of the tests' own definitions (`vehicle.h`): the values of every section line, and the rows of the lists. `test_fiz.cpp` converts them, using the game's parser for the includes, and checks the loader reads the same of the converted definition as of the game's file (`fiz.h`): the same sections with the same values, the same rows read by the same lists, in the same order. Comments, and lines the loader skips, are left out.
Convert them again, from the game data in `<game>/dynamic`, and add vehicles (paths as in the table, without the extension, separated by commas), with:
EU07_UNIT_TESTS_FIZ_SOURCE=<game>/dynamic EU07_UNIT_TESTS_FIZ_ADD=sm42_v1/6d1 eu07_unit_tests -tc="fiz: conversion*"
Every `.json` here is a vehicle picked up by `test_fiz.cpp`: record the parameters of an added one with `EU07_UNIT_TESTS_UPDATE_GOLDEN=1`.
| Path | Vehicle | Engine | Brake valve | Brake handle | Local brake | Coupler |
|---|---|---|---|---|---|---|
| `11xa_v2/93a.fiz` | 11XA | - | ESt4 | - | ManualBrake | Screw |
| `14xa_v1/173a.fiz` | 14XA | - | ESt4HBG300-s216 | - | ManualBrake | Screw |
| `168a_v1/168a.fiz` | 168A | - | ESt4HBG300 | - | ManualBrake | Screw |
| `21xm_v1/218ma_a_kd.fiz` | 218M | DieselEngine | EStED | MHZ_K5P | PneumaticBrake | Screw |
| `21xm_v1/214ma.fiz` | 214M | DieselEngine | EStED | MHZ_K5P | PneumaticBrake | Screw |
| `3wl_v2/3wl.fiz` | 3WL | - | ESt3 | - | ManualBrake | Screw |
| `430w_v2/430w.fiz` | 430W | - | ESt3AL2HBG300 | - | ManualBrake | Screw |
| `4e_v1/4e.fiz` | 4E | ElectricSeriesMotor | LSt | FV4a | PneumaticBrake | Screw |
| `bdhpumn_v3/bdhpumn.fiz` | BDHPUMN | - | KE | - | ManualBrake | Screw |
| `bhp_v1/bhp-b.fiz` | BHP | - | ESt3AL2 | - | ManualBrake | Articulated |
| `br285_v1/br285.fiz` | BR285 | DieselElectric | KE | MHZ_6P | PneumaticBrake | Screw |
| `cd/810_v2/810.fiz` | 810 | DieselEngine | ESt3_PZZ | FV4a | PneumaticBrake | Screw |
| `cd/754_v1/754.fiz` | 754 | DumbDE | LSt | FV4a | PneumaticBrake | Screw |
| `dr2_v1/dr2.fiz` | DR2 | WheelsDriven | - | - | ManualBrake | None |
| `e186_v2/eu47.fiz` | E186 | ElectricInductionMotor | EStED | MHZ_6P | PneumaticBrake | Screw |
| `e6act_v2/e6acta.fiz` | E6ACT | ElectricInductionMotor | EStED | MHZ_K5P | PneumaticBrake | Screw |
| `ed72_v1/5bs-rb.fiz` | ED72 | - | EP2 | FVel6 | ManualBrake | Automatic |
| `elf_v1/22wec-a.fiz` | ELF 22WEc | ElectricInductionMotor | EStED | MHZ_K5P | PneumaticBrake | Automatic |
| `elf_v1/21we-a.fiz` | ELF | ElectricInductionMotor | EStED | MHZ_K5P | PneumaticBrake | Automatic |
| `en57_v1/5bgb.fiz` | EN57 | - | EP2 | FVel6 | ManualBrake | Automatic |
| `en57_v1/6ba_840.fiz` | EN57 | - | EP2 | FVel6 | ManualBrake | Automatic |
| `en57_v1/6bai.fiz` | EN57 | - | K | St113 | ManualBrake | Automatic |
| `en57akl_v1/akl_ra.fiz` | EN57AKL | ElectricInductionMotor | EStED | MHZ_EN57 | PneumaticBrake | Automatic |
| `ep05_v2/ep05.fiz` | EP05 | ElectricSeriesMotor | LSt | FV4a | PneumaticBrake | Screw |
| `ep09_v2/104e.fiz` | EP09 | ElectricSeriesMotor | EStED | FV4a | PneumaticBrake | Screw |
| `et22_v3/201e-rn.fiz` | ET22 | ElectricSeriesMotor | LSt | FV4a | PneumaticBrake | Screw |
| `et22_v3/201e-w.fiz` | ET22 | ElectricSeriesMotor | LSt | FV4a | PneumaticBrake | Screw |
| `et42_v2/112e-a.fiz` | ET42 | ElectricSeriesMotor | LSt | FV4a | PneumaticBrake | Screw |
| `et42_v2/112e-b.fiz` | ET42 | ElectricSeriesMotor | LSt | FV4a | PneumaticBrake | Screw |
| `eu04_v1/eu04-01.fiz` | EU04 | ElectricSeriesMotor | K | Knorr | PneumaticBrake | Screw |
| `ew58_v1/ew58-d.fiz` | EW58 | - | EP1 | - | ManualBrake | Automatic |
| `ew58_v1/ew58-sa.fiz` | EW58 | ElectricSeriesMotor | EP1 | FVE408 | ManualBrake | Automatic |
| `impuls_v1/31we-a_pr.fiz` | IMPULS | ElectricInductionMotor | EStED | MHZ_6P | PneumaticBrake | Automatic |
| `road/man/nl293.fiz` | MAN (road) | DieselEngine | - | - | HydraulicBrake | Bare |
| `sa134_v1/218mc_a.fiz` | SA134 | DieselEngine | EStED | MHZ_K5P | PneumaticBrake | Screw |
| `sieciowiec_v1/a.fiz` | SIECIOWIEC | - | - | - | NoBrake | Screw |
| `sm03_v1/sm03.fiz` | SM03 | DieselEngine | K | Knorr | PneumaticBrake | Screw |
| `sm31_v1/411d.fiz` | SM31 | DumbDE | LSt | FV4a | PneumaticBrake | Screw |
| `sm40_v1/sm40.fiz` | SM40 | DieselElectric | K | Knorr | PneumaticBrake | Screw |
| `sm42_v1/6d1.fiz` | SM42 | DumbDE | LSt | FV4a | PneumaticBrake | Screw |
| `sn61_v2/sn61.fiz` | SN61 | DieselEngine | ESt3d_PZZ | FV4a | PneumaticBrake | Screw |
| `st44_v2/m62-1151.fiz` | ST44 | DieselElectric | LSt | FV4a | PneumaticBrake | Screw |
| `st45_v2/301dd.fiz` | ST45 | DumbDE | SW | MHZ_K8P | PneumaticBrake | Screw |
| `static/sug/sug_kielce.fiz` | SUG | ElectricSeriesMotor | W | Knorr | PneumaticBrake | Screw |
| `su45_v2/301d.fiz` | SU45 | DumbDE | LSt | FV4a | PneumaticBrake | Screw |
| `su46_v2/303d.fiz` | SU46 | DumbDE | LSt | FV4a | PneumaticBrake | Screw |
| `tem2_v2/tem2a.fiz` | TEM2 | DieselElectric | LSt | M394 | PneumaticBrake | Screw |
| `wm15_v1/wm15.fiz` | WM15 | Dumb | LSt | FV4a | PneumaticBrake | Screw |
| `wmb10_v1/wmb10.fiz` | WMB10 | DieselEngine | LSt | FV4a | PneumaticBrake | Screw |
| `z1_v2/152a.fiz` | Z1 | - | KE | - | ManualBrake | Screw |
The paths are those of the game's `.fiz` files. `elf_v1/inverter.fiz` is a part the Elf units include, not a vehicle. The 218M (V2NList), the SA134 (HTCList) and the 22WEc (ffBrakeList) are there for lists the others lack, the EP05 for its camshaft. The 214MA stands for the 214M railbus, whose own file repeats a section (TurboPos and MotorParamTable twice), which the definitions here can't hold.

View File

@@ -0,0 +1,14 @@
{
"_source": "pkp/bdhpumn_v3/bdhpumn.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "42200", "Vmax": "120", "HeatingP": "40", "LightP": "0.56"},
"Load": {"MaxLoad": "132", "LoadQ": "pieces", "LoadAccepted": "Passengers", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "26.8", "H": "4.7", "W": "2.88", "Cx": "0.15"},
"Wheels": {"D": "0.96", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.5", "Bd": "19.5", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "KE", "NBpA": "6", "MBF": "15", "MaxBP": "3.8", "BCN": "12", "BCR": "0.127", "BCD": "0.04", "BCM": "1.5", "BCS": "1.2", "BSA": "0", "BM": "Disk1", "BVV": "125", "TareMaxBP": "2.3"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "Mixed", "OpenSpeed": "0.5", "CloseSpeed": "1.0", "DoorCloseDelay": "0", "DoorMaxShiftL": "0.65", "DoorMaxShiftR": "0.65", "DoorOpenMethod": "Shift", "DoorMaxShiftPlug": "0.028", "DoorStayOpen": "5.0", "DoorClosureWarning": "no", "DoorBlocked": "Yes", "DoorAutoCloseRemote": "No", "DoorNeedPermit": "no"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.11", "FmaxB": "320", "kC": "4500", "DmaxC": "0.1", "FmaxC": "850", "beta": "0.58", "AllowedFlag": "115"},
"Cntrl": {"BrakeSystem": "Pneumatic", "LocalBrake": "ManualBrake", "BrakeDelays": "P", "MaxBPMass": "63", "rows": []},
"Light": {"Light": "Generator", "LGeneratorEngine": "WheelsDriven", "LMaxVoltage": "24", "AlterLight": "Accu", "AlterLMaxVoltage": "24", "AlterLCap": "495", "AlterLRS": "Generator"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "3000", "AlterHeating": "PowerCable", "AlterHPowerTrans": "SteamPower", "AlterHSteamPress": "0.45"}
}

View File

@@ -0,0 +1,15 @@
{
"_source": "pkp/bhp_v1/bhp-b.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "36000", "Vmax": "120", "HeatingP": "37.5", "LightP": "0.56"},
"Load": {"LoadAccepted": "Passengers,Luggage", "MaxLoad": "110", "LoadQ": "pieces", "OverLoadFactor": "2", "LoadSpeed": "3", "UnLoadSpeed": "4"},
"Dimensions": {"L": "19.75", "H": "4.750", "W": "2.720", "Cx": "0.4"},
"Wheels": {"D": "0.94", "AIM": "300", "Tw": "1.435", "Axle": "2'3'", "Ad": "3.0", "Bd": "17", "Rmin": "80", "BearingType": "Roll"},
"BuffCoupl1": {"CType": "Articulated", "AllowedFlag": "83", "PowerFlag": "256"},
"BuffCoupl2": {"CType": "Screw", "kB": "9200", "DmaxB": "0.11", "FmaxB": "320", "kC": "4500", "DmaxC": "0.1", "FmaxC": "850", "beta": "0.58", "AllowedFlag": "67"},
"Brake": {"BrakeValve": "ESt3AL2", "Size": "100", "MBF": "144", "MaxBP": "3.8", "TareMaxBP": "2.5", "BCN": "2", "BCR": "0.1403", "BCD": "0.14", "BCM": "10.38", "NBpA": "4", "BM": "P10-Bgu", "BCS": "2.7", "BSA": "5.00", "BVV": "100", "BRE": "0.85"},
"Cntrl": {"BrakeSystem": "Pneumatic", "LocalBrake": "ManualBrake", "BrakeDelays": "P", "MaxBPMass": "55", "rows": []},
"Doors": {"OpenCtrl": "Passenger", "CloseCtrl": "Mixed", "OpenSpeed": "0.55", "CloseSpeed": "0.75", "DoorMaxShiftL": "0.65", "DoorMaxShiftR": "0.65", "DoorOpenMethod": "Shift"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "3000", "AlterHeating": "PowerCable", "AlterHPowerTrans": "SteamPower", "AlterHSteamPress": "0.45"},
"Light": {"Light": "Generator", "LGeneratorEngine": "WheelsDriven", "LMaxVoltage": "24", "AlterLight": "Accu", "AlterLMaxVoltage": "24", "AlterLCap": "375", "AlterLRS": "Generator"}
}

View File

@@ -0,0 +1,69 @@
{
"_source": "pkp/br285_v1/br285.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "82400", "Mred": "12000", "Vmax": "140", "PWR": "2200", "SandCap": "700"},
"Load": {"MaxLoad": "52", "LoadQ": "pieces", "LoadAccepted": "pantstate", "LoadSpeed": "1", "UnLoadSpeed": "0.1"},
"Dimensions": {"L": "18.900", "H": "3.95", "W": "2.977", "Cx": "0.1"},
"Wheels": {"D": "1.250", "AIM": "4000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "1.75", "Bd": "10.3", "Rmin": "80"},
"SpringBrake": {"Volume": "10", "MBF": "8", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "3.5", "ValveOnArea": "0.0002", "ValveOffArea": "0.0002", "ValvePNBArea": "0.002", "MTC": "4"},
"BuffCoupl": {"ControlType": "br285", "CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "103"},
"Brake": {"BrakeValve": "KE", "NBpA": "4", "MBF": "50", "MaxBP": "3.8", "BCN": "8", "RM": "1.4", "BCR": "0.127", "BCD": "0.04", "BCM": "2.7", "BCS": "0.8", "BSA": "0", "BM": "Disk2", "BVV": "125", "Vv": "0.8", "CompressorSpeed": "0.04", "CompressorPower": "Converter", "MinCP": "8", "MaxCP": "10", "UBB3": "8", "LPOn": "3.0", "LPOff": "4.5", "HandlePipeUnlockPos": "2", "EmergencyCutsOffHandle": "Yes"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "3", "BDelay3": "35", "BDelay4": "36", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "MCPN": "6", "SCPN": "4", "AutoRelay": "Yes", "IniCDelay": "0.5", "SCDelay": "0.1", "BrakeDelays": "GPR", "BrakeHandle": "MHZ_6P", "LocBrakeHandle": "FD1", "ConverterStart": "Manual", "ConverterStartDelay": "3", "FuelStart": "Automatic", "OilStart": "Automatic", "BrakeOpModes": "PN", "EIMCtrlType": "3", "DirChangeMaxPos": "3", "HAO": "No", "HMO": "Yes", "HGDP1": "2", "rows": [
["-1","0.8","-1","20","Pneumatic"],
["0","0.5","-1","2","Pneumatic"],
["1","0.46","-1","2","Pneumatic"],
["2","0.42","-1","2","Pneumatic"],
["3","0.38","-1","2","Pneumatic"],
["4","0.34","-1","2","Pneumatic"],
["5","0.29","-1","2","Pneumatic"],
["6","0","-1","40","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal,SeparateAcknowledge", "AwareDelay": "30", "SoundSignalDelay": "2.5", "MaxHoldTime": "1.0", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "100"},
"Clima": {"HMaxVoltage": "3000", "Heating": "Generator", "HGeneratorEngine": "Main", "HGeneratorMinRPM": "600", "HGeneratorMinVoltage": "2400", "HGeneratorMaxRPM": "1860", "HGeneratorMaxVoltage": "3300"},
"SpeedControl": {"SpeedCtrl": "Yes", "OverrideManual": "No", "InitPwr": "1.0", "MaxPwrVel": "-1", "StartVel": "-1", "VelStep": "5", "PwrStep": "0.01", "MinPwr": "0.0", "MaxPwr": "1.0", "MinVel": "0", "MaxVel": "140", "Offset": "0", "kPpos": "0.1", "kPneg": "0.2", "kIpos": "0.03", "kIneg": "0.03", "BrakeIntervention": "No", "SpeedCtrlType": "Time", "SpeedCtrlDelay": "0.5"},
"Engine": {"EngineType": "DieselElectric", "Trans": "18:64", "Ftmax": "300000", "Vhyp": "24", "Vadd": "5.5", "Cr": "1", "WaterMinTemperature": "40", "WaterFlowTemperature": "70", "WaterCoolingTemperature": "82", "WaterMaxTemperature": "91", "WaterShutters": "Yes", "WaterAuxCircuit": "Yes", "WaterAuxCoolingTemperature": "50", "WaterAuxMaxTemperature": "74", "WaterAuxShutters": "Yes", "OilMinPressure": "0.15", "HeatKFO2": "33"},
"MotorParamTable": {"rows": [
["0","8.35","50","2000","48.2","0","0"]
]},
"Switches": {"Converter": "impulse"},
"WWList": {"Size": "6", "rows": [
["600","0","0","0"],
["800","600","1404","6000"],
["950","900","1404","6000"],
["1050","1100","1404","6000"],
["1250","1500","1404","6000"],
["1450","1850","1404","6000"],
["1860","2188","1404","6000"]
]},
"LightsList": {"Size": "12", "Wrap": "Yes", "Default": "1", "rows": [
["21","34"],
["34","0"],
["37","34"],
["21","34"],
["38","34"],
["22","34"],
["17","17"],
["17","17"],
["42","42"],
["4","4"],
["17","17"],
["16","1"]
]},
"WiperList": {"Size": "4", "Angle": "70", "rows": [
["0","0.8","0.0","0.5"],
["3","0.8","5.0","0.5"],
["3","0.8","0.5","0.0"],
["3","0.6","0.5","0.0"],
["endL"]
]},
"UCList": {"Size": "6", "IntegratedBrakePN": "no", "IntegratedLocBrake": "no", "IntegratedBrake": "no", "rows": [
["0","0","-1","0","-1","0.15","0.15","0","1","1"],
["1","0","-1","0","0","0.00","0.00","1","3","1"],
["2","0","-1","0","0","0.15","0.00","1","3","1"],
["3","0","0","0","0","0.00","0.00","1","5","3"],
["4","0","0","1","0","0.00","0.15","3","5","5"],
["5","0","0","1","0","0.00","0.00","3","6","5"],
["6","0","0","1","1","0.15","0.15","5","6","5"]
]}
}

View File

@@ -0,0 +1,56 @@
{
"_source": "cd/754_v1/754.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "74500", "Mred": "15000", "Vmax": "100", "PWR": "1460", "SandCap": "400", "HeatingP": "300"},
"Dimensions": {"L": "16.54", "H": "4.335", "W": "2.900", "Cx": "-0.1"},
"Wheels": {"D": "1.000", "AIM": "4000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "2.4", "Bd": "9.0", "Rmin": "100"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.1", "LocMaxBP": "4.5", "MaxASBP": "1.5", "RM": "0.69", "BCN": "8", "BCR": "0.114", "BCD": "0.06", "BCM": "8.1", "HiPP": "5", "LoPP": "3.5", "Vv": "0.8", "MinCP": "7.0", "MaxCP": "8.5", "CompressorSpeed": "0.069", "BCS": "1", "BSA": "2", "BM": "P10-Bgu", "CompressorPower": "Converter", "BVV": "240", "BRE": "0.9"},
"BuffCoupl": {"ControlType": "cd754", "CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "103"},
"Cntrl": {"BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "6", "BDelay3": "36", "BDelay4": "32", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "MCPN": "14", "SCPN": "3", "AutoRelay": "Yes", "IniCDelay": "0.5", "SCDelay": "0.1", "BrakeDelays": "GPR", "rows": [
["-1","0.7","-1","15","Pneumatic"],
["0","0.5","-1","9","Pneumatic"],
["1","0.46","-1","9","Pneumatic"],
["2","0.42","-1","9","Pneumatic"],
["3","0.38","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.29","-1","9","Pneumatic"],
["6","0","-1","15","Pneumatic"]
]},
"LightsList": {"Size": "8", "Wrap": "No", "Default": "1", "rows": [
["0","0"],
["388","34"],
["21","34"],
["1","16"],
["16","1"],
["22","34"],
["38","38"],
["34","34"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "227.8"},
"Engine": {"EngineType": "DumbDE", "Trans": "18:62", "Ftmax": "215000", "Flat": "0", "Vhyp": "7.2", "Vadd": "5.5", "Cr": "1.0", "RelayType": "0"},
"Clima": {"Heating": "Main"},
"MotorParamTable": {"rows": [
["0","20","100","3000","20.8","1340","1713"],
["1","18.5","110","2700","25","1340","1713"],
["2","17.2","135","2500","30","1340","1713"],
["3","14.3","170","2000","48.5","1340","1713"]
]},
"WWList": {"Size": "14", "rows": [
["500","0","0","0"],
["500","0","0","0"],
["550","122","78","273"],
["550","122","78","273"],
["600","244","147","547"],
["650","365","221","820"],
["700","287","295","1094"],
["750","609","369","1367"],
["800","730","442","1641"],
["850","852","516","1914"],
["900","973","590","2187"],
["950","1095","664","2461"],
["1000","1216","737","2734"],
["1050","1338","811","3007"],
["1100","1460","885","3281"]
]}
}

View File

@@ -0,0 +1,41 @@
{
"_source": "cd/810_v2/810.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "Type": "dmu", "M": "20000", "Vmax": "80", "PWR": "155", "SandCap": "25"},
"Load": {"MaxLoad": "55", "OverLoadFactor": "2.2", "LoadQ": "pieces", "LoadAccepted": "passengers", "LoadSpeed": "0.5", "UnLoadSpeed": "0.7"},
"Dimensions": {"L": "13.86", "H": "2.8", "W": "3.00", "Cx": "0.38"},
"Wheels": {"D": "0.84", "Tw": "1.435", "AIM": "300", "Axle": "1'A'", "Bd": "8", "Rmin": "100", "BearingType": "Roll"},
"Brake": {"BrakeType": "Oerlikon", "BrakeValve": "ESt3_PZZ", "Size": "100", "NBpA": "4", "MBF": "45.0", "MaxBP": "3.8", "BCN": "2", "BCR": "0.127", "BCD": "0.14", "BCM": "3.25", "HiPP": "5.0", "LoPP": "3.5", "Vv": "0.257", "MinCP": "7.0", "MaxCP": "8.0", "CompressorSpeed": "0.0136", "BM": "P10-Bg", "BVV": "75", "BRE": "0.87", "AirLeakRate": "1.15", "CompressorPower": "Converter"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "3"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.35", "CloseSpeed": "0.625", "DoorMaxShiftL": "0.9", "DoorMaxShiftR": "0.9", "DoorOpenMethod": "Plug", "DoorClosureWarning": "No", "DoorBlocked": "Yes", "DoorMaxShiftPlug": "0.02", "DoorNeedPermit": "Yes", "DoorPermitList": "1|0|2", "DoorPermitListDefault": "2", "DoorAutoCloseRemote": "No"},
"Cntrl": {"BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "MCPN": "8", "SCPN": "2", "AutoRelay": "Yes", "IniCDelay": "0.5", "SCDelay": "0.3", "ManualBrake": "Yes", "WaterStart": "Battery", "OilStart": "Automatic", "FuelStart": "Automatic", "ConverterStart": "Automatic", "ConverterStartDelay": "4", "CompartmentLightsStart": "Manual", "rows": [
["-1","1.0","-1","12","Pneumatic"],
["0","0.5","-1","9","Pneumatic"],
["1","0.43","-1","9","Pneumatic"],
["2","0.4","-1","9","Pneumatic"],
["3","0.37","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.3","-1","9","Pneumatic"],
["6","0","-1","16","Pneumatic"]
]},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"HMaxVoltage": "3000", "Heating": "Generator", "HGeneratorEngine": "Main", "HGeneratorMinRPM": "600", "HGeneratorMinVoltage": "2400", "HGeneratorMaxRPM": "2150", "HGeneratorMaxVoltage": "3300"},
"Security": {"AwareSystem": "Active", "AwareDelay": "30", "SoundSignalDelay": "0.1", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "5", "RadioStop": "No"},
"Engine": {"EngineType": "DieselEngine", "Trans": "13:51", "nmin": "600", "nmax": "2150", "nmax_cutoff": "2500", "AIM": "60", "EUS": "2.5", "EDS": "2.5", "IsTC": "Yes", "TC_TMMax": "2.70", "TC_CP": "0.85", "TC_LT": "850", "TC_LR": "1", "TC_ULR": "1", "TC_FRI": "1", "TC_FRD": "1", "TC_TII": "0.60", "TC_TIO": "0", "TC_TOO": "0", "TC_LS": "55", "TC_ULS": "45", "IsRetarder": "No", "WaterMaxTemperature": "105", "WaterCoolingTemperature": "90", "WaterFlowTemperature": "75", "WaterAuxCircuit": "No", "WaterCoolingFanSpeed": "-100", "NominalCoolingPower": "154"},
"MotorParamTable": {"minVelfullengage": "-0.1", "engageDia": "1", "engageMaxForce": "1900", "engagefriction": "1", "rows": [
["0","0","0","-1","1"],
["1","2.120","-1","33","1"],
["2","1.000","25","200","1"]
]},
"DList": {"Size": "8", "Mmax": "850", "nMmax": "29", "nmax": "41", "Mnmax": "750", "nominalfill": "0.9", "Mstand": "100.0", "rows": [
["0","0.1","0"],
["1","0.2","1"],
["2","0.3","2"],
["3","0.4","2"],
["4","0.5","2"],
["5","0.6","2"],
["6","0.7","2"],
["7","0.8","2"],
["8","1.0","2"]
]}
}

View File

@@ -0,0 +1,13 @@
{
"_source": "pkp/dr2_v1/dr2.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "300", "Vmax": "40", "PWR": "6"},
"Dimensions": {"L": "1.65", "H": "0.79", "W": "1.47", "Cx": "0.058"},
"Load": {"MaxLoad": "3", "LoadQ": "pieces", "LoadAccepted": "Passengers", "LoadSpeed": "0.5", "UnLoadSpeed": "0.5"},
"Wheels": {"D": "0.4", "AIM": "4.8", "Tw": "1.435", "Axle": "1'A", "Bd": "0.86", "Rmin": "20", "BearingType": "Slide"},
"BuffCoupl": {"CType": "None"},
"Brake": {"BrakeType": "Individual", "MBF": "0.2"},
"Cntrl": {"BrakeSystem": "Individual", "LocalBrake": "ManualBrake", "MCPN": "1", "SCDelay": "0.2", "rows": []},
"Power": {"EnginePower": "Internal", "PowerType": "BioPower"},
"Engine": {"EngineType": "WheelsDriven", "Trans": "9:2", "Ftmax": "1200"}
}

View File

@@ -0,0 +1,61 @@
{
"_source": "pkp/e186_v2/eu47.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "81000", "Mred": "9000", "Vmax": "160", "PWR": "5600", "SandCap": "760"},
"Load": {"MaxLoad": "52", "LoadQ": "pieces", "LoadAccepted": "pantstate", "LoadSpeed": "1", "UnLoadSpeed": "0.1"},
"Dimensions": {"L": "18.900", "H": "3.95", "W": "2.977", "Cx": "0.1"},
"Wheels": {"D": "1.250", "AIM": "4000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "1.75", "Bd": "10.3", "Rmin": "100"},
"Brake": {"BrakeValve": "EStED", "NBpA": "4", "MBF": "50", "MaxBP": "3.8", "BCN": "8", "RM": "1.4", "BCR": "0.127", "BCD": "0.04", "BCM": "2.7", "BCS": "0.8", "BSA": "0", "BM": "Disk2", "BVV": "125", "Vv": "0.8", "CompressorSpeed": "0.04", "CompressorPower": "Converter", "MinCP": "8", "MaxCP": "10", "UBB3": "8", "LPOn": "3.0", "LPOff": "4.5", "HandlePipeUnlockPos": "2", "EmergencyCutsOffHandle": "Yes"},
"SpringBrake": {"Volume": "10", "MBF": "8", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "3.5", "ValveOnArea": "0.0002", "ValveOffArea": "0.0002", "ValvePNBArea": "0.002", "MTC": "4"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "103", "ControlType": "EU47"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BrakeOpModes": "PN", "BCPN": "4", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Automatic", "LocalBrake": "PneumaticBrake", "MCPN": "7", "SCPN": "4", "AutoRelay": "No", "MainInitTime": "10", "IniCDelay": "0.5", "SCDelay": "0.1", "BrakeDelays": "GPR", "BrakeHandle": "MHZ_6P", "LocBrakeHandle": "FD1", "ScndS": "Yes", "SBD": "0", "ConverterStart": "Automatic", "ConverterStartDelay": "10", "PantCompressorStart": "Automatic", "PantAutoValve": "Yes", "EIMCtrlType": "1", "HAO": "No", "HMO": "No", "HGDP1": "0.0", "HGDP2": "1", "OMP": "0.4", "DirChangeMaxPos": "3", "LocalBrakeTraxx": "Yes", "PantEPValveStart": "Manual", "PantEPValveSpring": "No", "rows": [
["-1","0.8","-1","20","Pneumatic"],
["0","0.5","-1","2","Pneumatic"],
["1","0.46","-1","2","Pneumatic"],
["2","0.42","-1","2","Pneumatic"],
["3","0.38","-1","2","Pneumatic"],
["4","0.34","-1","2","Pneumatic"],
["5","0.29","-1","2","Pneumatic"],
["6","0","-1","40","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal,SeparateAcknowledge", "AwareDelay": "30", "SoundSignalDelay": "2.5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "5", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "4", "MaxVoltage": "3900", "MaxCurrent": "2000", "MinH": "0.48", "MinV": "1900", "MaxH": "2.5", "CSW": "1.5", "PhysicalLayout": "3"},
"Engine": {"EngineType": "ElectricInductionMotor", "Trans": "48:251", "dfic": "861", "dfmax": "1.84", "p": "2", "cfu": "43.7", "cim": "13.4", "icif": "0.679", "Uzmax": "2183", "Uzh": "2183", "DU": "20", "I0": "20", "fcfu": "43.7", "F0": "300", "a1": "0.4", "Pmax": "5600", "Fh": "150", "Ph": "2600", "Vh0": "5", "Vh1": "10", "Imax": "1950", "abed": "1", "Flat": "Yes"},
"Switches": {"Pantograph": "Impulse"},
"SpeedControl": {"SpeedCtrl": "Yes", "OverrideManual": "No", "InitPwr": "1.0", "MaxPwrVel": "-1", "StartVel": "-1", "VelStep": "5", "PwrStep": "0.01", "MinPwr": "0.0", "MaxPwr": "1.0", "MinVel": "0", "MaxVel": "160", "Offset": "0", "kPpos": "0.1", "kPneg": "0.2", "kIpos": "0.03", "kIneg": "0.03", "BrakeIntervention": "No", "SpeedCtrlType": "Time", "SpeedCtrlDelay": "0.5"},
"Doors": {"DoorPermitList": "0|3", "DoorPermitListDefault": "1"},
"Circuit": {"ImaxHi": "500"},
"ffList": {"Size": "7", "rows": [
["0","0.4"],
["6.3","0.4"],
["7.1","0.6"],
["25.6","0.6"],
["26.4","0.7"],
["29.6","0.7"],
["30.4","1.0"],
["60.8","2.0"]
]},
"LightsList": {"Size": "12", "Wrap": "Yes", "Default": "1", "rows": [
["21","34"],
["34","0"],
["37","34"],
["21","34"],
["38","34"],
["22","34"],
["17","17"],
["17","17"],
["42","42"],
["4","4"],
["17","17"],
["16","1"]
]},
"WiperList": {"Size": "4", "Angle": "70", "rows": [
["0","0.8","0.0","0.5"],
["3","0.8","5.0","0.5"],
["3","0.8","0.5","0.0"],
["3","0.6","0.5","0.0"],
["endL"]
]}
}

View File

@@ -0,0 +1,41 @@
{
"_source": "pkp/e6act_v2/e6acta.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "115000", "Mred": "9000", "Vmax": "120", "PWR": "5004", "SandCap": "700"},
"Load": {"MaxLoad": "52", "LoadQ": "pieces", "LoadAccepted": "pantstate", "LoadSpeed": "1", "UnLoadSpeed": "0.1"},
"Dimensions": {"L": "20.33", "H": "4.325", "W": "3.0", "Cx": "0.1"},
"Wheels": {"D": "1.250", "AIM": "4000", "Tw": "1.435", "Axle": "Co'Co'", "Ad": "3.9", "Bd": "14.2", "Rmin": "150", "BearingType": "Roll"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "35"},
"Brake": {"BrakeValve": "EStED", "NBpA": "4", "MBF": "50", "MaxBP": "4.4", "BCN": "12", "MaxLBP": "4.4", "RM": "1.4", "BCR": "0.127", "BCD": "0.04", "BCM": "2.7", "BCS": "0.8", "BSA": "0", "BRE": "0.97", "BM": "P10-Bgu", "BVV": "100", "Vv": "0.8", "CompressorSpeed": "0.04", "MinCP": "8", "MaxCP": "10", "DynamicBrake": "Automatic", "LPOn": "3.0", "LPOff": "4.5", "UBB1": "8", "UBB2": "1", "HandlePipeUnlockPos": "0", "EmergencyCutsOffHandle": "Yes"},
"SpringBrake": {"Volume": "10", "MBF": "2.9", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "4.2", "ValveOnArea": "0.0005", "ValveOffArea": "0.0005", "ValvePNBArea": "0.002", "MTC": "4"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Automatic", "LocalBrake": "PneumaticBrake", "MCPN": "10", "SCPN": "12", "AutoRelay": "No", "MainInitTime": "5", "IniCDelay": "0.5", "SCDelay": "0.1", "BrakeDelays": "GP", "BrakeHandle": "MHZ_K5P", "LocBrakeHandle": "FD1", "ConverterStart": "Automatic", "ConverterStartDelay": "10", "PantCompressorStart": "Mixed", "PantAutoValve": "Yes", "LocalBrakeTraxx": "Yes", "HAO": "No", "HMO": "No", "HGDP1": "0.0", "HGDP2": "1", "BrakeOpModes": "PN", "AutomaticCabActivation": "No", "InactivaCabEmergencyBrake": "Yes", "InactiveCabFlag": "15", "rows": [
["-1","0.8","-1","20","Pneumatic"],
["0","0.5","-1","2","Pneumatic"],
["1","0.46","-1","2","Pneumatic"],
["2","0.42","-1","2","Pneumatic"],
["3","0.38","-1","2","Pneumatic"],
["4","0.34","-1","2","Pneumatic"],
["5","0.29","-1","2","Pneumatic"],
["6","0","-1","40","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "2.4", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "2.1", "CabDependent": "Yes", "RadioStop": "Yes"},
"Circuit": {"ImaxHi": "500"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "2", "MaxVoltage": "4600", "MaxCurrent": "1600", "MinH": "0.8", "MaxH": "1.8", "CSW": "1.5", "MinV": "2000", "InsetV": "2000", "PhysicalLayout": "3"},
"Engine": {"EngineType": "ElectricInductionMotor", "Trans": "54:253", "dfic": "1021", "dfmax": "0.92", "p": "2", "cfu": "48.9", "cim": "15", "icif": "0.664", "Uzmax": "2150", "Uzh": "2150", "DU": "20", "I0": "20", "fcfu": "48.9", "F0": "420", "a1": "1.53", "Pmax": "5100", "Fh": "200", "Ph": "3000", "Vh0": "5", "Vh1": "10", "Imax": "1870", "abed": "1", "eped": "10"},
"SpeedControl": {"SpeedCtrl": "Yes", "OverrideManual": "No", "InitPwr": "1.0", "MaxPwrVel": "-1", "StartVel": "-1", "VelStep": "10", "PwrStep": "0.01", "MinPwr": "0.0", "MaxPwr": "1.0", "MinVel": "0", "MaxVel": "120", "Offset": "-0.1", "kPpos": "0.2", "kPneg": "0.4", "kIpos": "0.02", "kIneg": "0.00", "BrakeIntervention": "No", "SpeedCtrlDelay": "0.5"},
"ffList": {"Size": "2", "rows": [
["0","0.40"],
["19.6","0.805"],
["25.7","1.055"]
]},
"LightsList": {"Size": "7", "Wrap": "no", "Default": "4", "rows": [
["16","1"],
["21","34"],
["22","34"],
["0","0"],
["34","34"],
["17","34"],
["38","34"]
]}
}

View File

@@ -0,0 +1,25 @@
{
"_source": "pkp/ed72_v1/5bs-ra.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "35500", "Vmax": "110", "PWR": "0", "SandCap": "0", "Type": "ezt"},
"Load": {"MaxLoad": "54", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "1"},
"Dimensions": {"L": "20.87", "H": "4", "W": "2.9", "Cx": "0.2"},
"Wheels": {"D": "0.94", "AIM": "2000", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.83", "Bd": "13.9", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EP2", "NBpA": "4", "MBF": "85.0", "MaxBP": "4", "BCN": "1", "BCR": "0.203", "BCD": "0.15", "BCM": "9.39", "HiPP": "5", "LoPP": "3.5", "Vv": "0.3", "MinCP": "5.8", "MaxCP": "7.2", "CompressorSpeed": "0.013", "TareMaxBP": "2.5", "BVV": "88", "BM": "P10-Bgu", "BCS": "1.5", "BSA": "5", "BRE": "0.9", "CompressorPower": "Coupler2"},
"Doors": {"OpenCtrl": "DriverCtrl", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.4", "CloseSpeed": "0.4", "DoorCloseDelay": "0.5", "DoorMaxShiftL": "0.625", "DoorMaxShiftR": "0.625", "DoorOpenMethod": "Shift", "DoorClosureWarning": "Yes", "DoorBlocked": "Yes"},
"BuffCoupl1": {"ControlType": "ed72", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "AutomaticFlag": "39"},
"BuffCoupl2": {"ControlType": "ed72", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-119"},
"Cntrl": {"BrakeHandle": "FVel6", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "52", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "ASB": "Manual", "LocalBrake": "ManualBrake", "MCPN": "3", "SCPN": "3", "IniCDelay": "0.0", "SCDelay": "0.975", "CoupledCtrl": "Yes", "ScndS": "Yes", "BatteryStart": "Disabled", "ConverterStart": "Disabled", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes", "MagnetLocation": "5"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Switches": {"Pantograph": "impulse", "Converter": "impulse"}
}

View File

@@ -0,0 +1,25 @@
{
"_source": "pkp/ed72_v1/5bs-rb.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "35500", "Vmax": "110", "PWR": "0", "SandCap": "0", "Type": "ezt"},
"Load": {"MaxLoad": "54", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "1"},
"Dimensions": {"L": "20.87", "H": "4", "W": "2.9", "Cx": "0.2"},
"Wheels": {"D": "0.94", "AIM": "2000", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.83", "Bd": "13.9", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EP2", "NBpA": "4", "MBF": "85.0", "MaxBP": "4", "BCN": "1", "BCR": "0.203", "BCD": "0.15", "BCM": "9.39", "HiPP": "5", "LoPP": "3.5", "Vv": "0.3", "MinCP": "5.8", "MaxCP": "7.2", "CompressorSpeed": "0.013", "TareMaxBP": "2.5", "BVV": "88", "BM": "P10-Bgu", "BCS": "1.5", "BSA": "5", "BRE": "0.9", "CompressorPower": "Coupler1"},
"Doors": {"OpenCtrl": "DriverCtrl", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.315", "CloseSpeed": "0.325", "DoorMaxShiftL": "0.625", "DoorCloseDelay": "1.6", "DoorMaxShiftR": "0.625", "DoorOpenMethod": "Shift", "DoorClosureWarning": "Yes", "DoorBlocked": "Yes"},
"BuffCoupl1": {"ControlType": "ed72", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-119"},
"BuffCoupl2": {"ControlType": "ed72", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "AutomaticFlag": "39"},
"Cntrl": {"BrakeHandle": "FVel6", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "52", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "ASB": "Manual", "LocalBrake": "ManualBrake", "MCPN": "3", "SCPN": "3", "IniCDelay": "0.0", "SCDelay": "0.975", "CoupledCtrl": "Yes", "ScndS": "Yes", "ConverterStart": "Disabled", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes", "MagnetLocation": "5"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Switches": {"Pantograph": "impulse", "Converter": "impulse"}
}

View File

@@ -0,0 +1,47 @@
{
"_source": "pkp/ed72_v1/6bs-sa.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "55500", "Vmax": "110", "PWR": "608", "SandCap": "500", "Type": "ezt"},
"Load": {"MaxLoad": "76", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "21.77", "H": "4.12", "W": "2.96", "Cx": "0.1"},
"Wheels": {"D": "1.0", "AIM": "2000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "2.75", "Bd": "15.6", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EP2", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.1", "BCN": "2", "BCR": "0.178", "BCD": "0.15", "BCM": "7.32", "HiPP": "5", "LoPP": "3.5", "Vv": "0.1", "MinCP": "5.8", "MaxCP": "7.2", "CompressorSpeed": "0.0", "TareMaxBP": "2.8", "BVV": "100", "BM": "P10-Bgu", "BCS": "1.5", "BSA": "4", "BRE": "0.9"},
"Doors": {"OpenCtrl": "DriverCtrl", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.38", "CloseSpeed": "0.475", "DoorCloseDelay": "2.0", "DoorMaxShiftL": "0.625", "DoorMaxShiftR": "0.625", "DoorOpenMethod": "Shift", "DoorClosureWarning": "Yes", "DoorBlocked": "Yes"},
"BuffCoupl1": {"ControlType": "ed72", "CType": "Automatic", "kB": "19200", "DmaxB": "0.05", "FmaxB": "740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-119"},
"BuffCoupl2": {"ControlType": "ed72", "CType": "Automatic", "kB": "19200", "DmaxB": "0.05", "FmaxB": "740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-127"},
"Cntrl": {"BrakeSystem": "ElectroPneumatic", "MaxBPMass": "75", "BCPN": "1", "BDelay1": "15", "BDelay2": "4", "ASB": "Manual", "DynamicBrake": "Passive", "MCPN": "3", "SCPN": "3", "AutoRelay": "Yes", "IniCDelay": "0.0", "SCDelay": "0.975", "CoupledCtrl": "Yes", "ScndS": "Yes", "BatteryStart": "Disabled", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","-1","-1","0","ElectroPneumatic"],
["1","-1","0.35","4","ElectroPneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "1", "MaxVoltage": "3600", "MaxCurrent": "1200", "MinH": "0.38", "MinV": "1900", "MaxH": "2.4", "CSW": "1.4", "PhysicalLayout": "1"},
"Engine": {"EngineType": "ElectricSeriesMotor", "Volt": "1500", "Trans": "19:70", "WindingRes": "0.3836", "nmax": "2390"},
"Switches": {"Pantograph": "Impulse"},
"MotorParamTable0": {"rows": [
["0","21.52","81","-0.02","148","81","-0.02","1"],
["1","21.68","120","-0.02","148.2","120","-0.02","1"],
["2","21.17","130","0.01","142.3","130","0.01","1"],
["3","22.46","70","0.24","150.1","70","0.24","0"]
]},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "280", "ImaxHi": "281", "IminLo": "135", "IminHi": "175"},
"RList": {"Size": "14", "RVent": "No", "rows": [
["0","0","0","0","0"],
["1","17.682","1","4","0"],
["2","14.182","1","4","1"],
["2","10.682","1","4","1"],
["2","8.474","1","4","1"],
["2","6.266","1","4","1"],
["2","4.605","1","4","1"],
["2","2.944","1","4","1"],
["2","1.472","1","4","1"],
["2","0","1","4","1"],
["3","8.841","2","2","1"],
["3","5.341","2","2","1"],
["3","3.133","2","2","1"],
["3","1.472","2","2","1"],
["3","0","2","2","0"]
]}
}

View File

@@ -0,0 +1,47 @@
{
"_source": "pkp/ed72_v1/6bs-sb.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "55500", "Vmax": "110", "PWR": "608", "SandCap": "500", "Type": "ezt"},
"Load": {"MaxLoad": "76", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "21.77", "H": "4.12", "W": "2.96", "Cx": "0.1"},
"Wheels": {"D": "1.0", "AIM": "2000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "2.75", "Bd": "15.6", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EP2", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.1", "BCN": "2", "BCR": "0.178", "BCD": "0.15", "BCM": "7.32", "HiPP": "5", "LoPP": "3.5", "Vv": "0.1", "MinCP": "5.8", "MaxCP": "7.2", "CompressorSpeed": "0.0", "TareMaxBP": "2.8", "BVV": "100", "BM": "P10-Bgu", "BCS": "1.5", "BSA": "4", "BRE": "0.9"},
"Doors": {"OpenCtrl": "DriverCtrl", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.335", "CloseSpeed": "0.34", "DoorCloseDelay": "1.5", "DoorMaxShiftL": "0.625", "DoorMaxShiftR": "0.625", "DoorOpenMethod": "Shift", "DoorClosureWarning": "Yes", "DoorBlocked": "Yes"},
"BuffCoupl1": {"ControlType": "ed72", "CType": "Automatic", "kB": "19200", "DmaxB": "0.05", "FmaxB": "740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-127"},
"BuffCoupl2": {"ControlType": "ed72", "CType": "Automatic", "kB": "19200", "DmaxB": "0.05", "FmaxB": "740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-119"},
"Cntrl": {"BrakeSystem": "ElectroPneumatic", "MaxBPMass": "75", "BCPN": "1", "BDelay1": "15", "BDelay2": "4", "ASB": "Manual", "DynamicBrake": "Passive", "MCPN": "3", "SCPN": "3", "AutoRelay": "Yes", "IniCDelay": "0.0", "SCDelay": "0.975", "CoupledCtrl": "Yes", "ScndS": "Yes", "BatteryStart": "Disabled", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","-1","-1","0","ElectroPneumatic"],
["1","-1","0.35","4","ElectroPneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "2", "MaxVoltage": "3600", "MaxCurrent": "1200", "MinH": "0.38", "MinV": "1900", "MaxH": "2.4", "CSW": "1.4", "PhysicalLayout": "2"},
"Engine": {"EngineType": "ElectricSeriesMotor", "Volt": "1500", "Trans": "19:70", "WindingRes": "0.3836", "nmax": "2390"},
"Switches": {"Pantograph": "Impulse"},
"MotorParamTable0": {"rows": [
["0","21.52","81","-0.02","148","81","-0.02","1"],
["1","21.68","120","-0.02","148.2","120","-0.02","1"],
["2","21.17","130","0.01","142.3","130","0.01","1"],
["3","22.46","70","0.24","150.1","70","0.24","0"]
]},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "280", "ImaxHi": "281", "IminLo": "135", "IminHi": "175"},
"RList": {"Size": "14", "RVent": "No", "rows": [
["0","0","0","0","0"],
["1","17.682","1","4","0"],
["2","14.182","1","4","1"],
["2","10.682","1","4","1"],
["2","8.474","1","4","1"],
["2","6.266","1","4","1"],
["2","4.605","1","4","1"],
["2","2.944","1","4","1"],
["2","1.472","1","4","1"],
["2","0","1","4","1"],
["3","8.841","2","2","1"],
["3","5.341","2","2","1"],
["3","3.133","2","2","1"],
["3","1.472","2","2","1"],
["3","0","2","2","0"]
]}
}

View File

@@ -0,0 +1,52 @@
{
"_source": "pkp/elf_v1/21we-a.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "28650", "Vmax": "160", "PWR": "800", "SandCap": "500", "Type": "ezt", "HeatingP": "37"},
"Load": {"MaxLoad": "108", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2.3", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "21.081", "H": "4.228", "W": "2.96", "Cx": "0.1"},
"Wheels": {"D": "0.850", "AIM": "8000", "Tw": "1.435", "Axle": "Bo'", "Ad": "2.83", "Bd": "16.3", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EStED", "NBpA": "4", "MBF": "85.0", "MaxBP": "3.1", "MaxLBP": "4.2", "RM": "0.74", "RV": "-1", "BCN": "4", "BCR": "0.127", "BCD": "0.05", "BCM": "1.9075", "HiPP": "5", "LoPP": "3.5", "Vv": "0.5", "MinCP": "7.7", "MaxCP": "9.0", "CompressorSpeed": "0.025", "BVV": "150", "BM": "Cosid", "BCS": "1", "BSA": "0.00", "BRE": "0.95", "TareMaxBP": "2.6", "CompressorPower": "Converter", "LPOn": "3.0", "LPOff": "4.5", "UBB1": "2", "UBB2": "8"},
"SpringBrake": {"Volume": "10", "MBF": "2.9", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "4.2", "ValveOnArea": "0.0005", "ValveOffArea": "0.0005", "ValvePNBArea": "0.002", "MTC": "4"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.24", "CloseSpeed": "0.2", "DoorMaxShiftL": "0.74", "DoorMaxShiftR": "0.74", "DoorOpenMethod": "Plug", "DoorStayOpen": "5.0", "DoorClosureWarningAuto": "Yes", "DoorBlocked": "Yes", "DoorMaxShiftPlug": "0.05", "DoorCloseDelay": "1.8", "PlatformMaxShift": "0.270", "PlatformSpeed": "0.4", "PlatformOpenMethod": "Shift", "MirrorMaxShift": "45", "DoorNeedPermit": "Yes", "DoorPermitList": "1|0|2|3", "DoorPermitListDefault": "2", "DoorAutoCloseRemote": "Yes", "MirrorVelClose": "25"},
"BuffCoupl1": {"CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "AutomaticFlag": "39", "ControlType": "en62"},
"BuffCoupl2": {"CType": "Articulated", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-255"},
"Cntrl": {"BrakeHandle": "MHZ_K5P", "LocBrakeHandle": "FD1", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "37", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "LocalBrake": "PneumaticBrake", "MCPN": "4", "EIMCtrlType": "2", "SCPN": "1", "MainInitTime": "10", "IniCDelay": "1.0", "SCDelay": "0.2", "CoupledCtrl": "No", "SBD": "0.6", "ASB": "Yes", "CompressorStart": "Automatic", "ConverterStart": "Automatic", "ConverterStartDelay": "10.0", "PantCompressorStart": "Mixed", "PantAutoValve": "Yes", "BrakeDelays": "PR", "BrakeOpModes": "PN", "DirChangeMaxPos": "3", "HAO": "No", "HMO": "Yes", "HGDP1": "0", "ReleaseParkingBySpringBrake": "Yes", "MotorBlowersStart": "Converter", "CompartmentLightsStart": "Manual", "AutomaticCabActivation": "No", "InactivaCabEmergencyBrake": "Yes", "InactiveCabFlag": "3", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Circuit": {"ImaxHi": "500"},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "2.4", "MaxHoldTime": "0.1", "EmergencyBrakeDelay": "2.1", "CabDependent": "Yes", "RadioStop": "Yes", "EmergencyBrakeWarningSignal": "2", "MagnetLocation": "4.5"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main", "HMaxVoltage": "400"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "1", "MaxVoltage": "4000", "MinV": "2000", "MaxCurrent": "1200", "MinH": "0.38", "MaxH": "2.4", "CSW": "1.4", "PhysicalLayout": "1"},
"Blending": {"MED_amax": "1.04", "MED_EPVC": "No", "MED_Ncor": "No", "MED_FirstDelayED": "2", "MED_ScndDelayED": "1"},
"Engine": {"EngineType": "ElectricInductionMotor", "Trans": "40:218", "dfic": "470", "dfmax": "0.790", "p": "2", "cfu": "33.43", "cim": "10.22", "icif": "0.67", "Uzmax": "2340", "Uzh": "2340", "DU": "20", "I0": "20", "fcfu": "33.5", "F0": "75", "a1": "0.0", "Pmax": "980", "Fh": "48", "Ph": "830", "Vh0": "2", "Vh1": "5", "Imax": "377", "abed": "0", "Flat": "Yes", "edep": "1.0", "MotorBlowersSpeed": "-29", "InvNo": "2", "InvCtrCplFlag": "255"},
"SpeedControl": {"SpeedCtrl": "Yes", "SpeedButtons": "0|40|50|60|70|80|100|120|140|160", "OverrideManual": "Yes", "InitPwr": "0.3", "MaxPwrVel": "5", "StartVel": "2", "VelStep": "5", "PwrStep": "0.1", "MinPwr": "0.3", "MaxPwr": "1.0", "MinVel": "0", "MaxVel": "160", "Offset": "-0.5", "kPpos": "0.1", "kPneg": "0.4", "kIpos": "0.06", "kIneg": "0.06", "BrakeIntervention": "No", "PowerUpSpeed": "0.3", "PowerDownSpeed": "0.3"},
"AI": {"Pantstate": "1"},
"ffList": {"Size": "10", "rows": [
["0","2.49"],
["22.1","2.49"],
["24.1","2.50"],
["31.3","2.50"],
["32.3","2.53"],
["39.3","3.0"],
["65","5.0"],
["91","7.0"],
["124","9.0"],
["161","11.7"],
["177","13.0"]
]},
"LightsList": {"Size": "5", "Wrap": "no", "Default": "1", "rows": [
["0","0"],
["21","34"],
["22","34"],
["34","34"],
["16","1"]
]}
}

View File

@@ -0,0 +1,25 @@
{
"_source": "pkp/elf_v1/21we-b.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "50000", "Vmax": "160", "PWR": "0", "SandCap": "500", "Type": "ezt", "HeatingP": "37"},
"Load": {"MaxLoad": "108", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2.3", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "16.5", "H": "4.12", "W": "2.96", "Cx": "0.1"},
"Wheels": {"D": "0.850", "AIM": "670", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.83", "Bd": "16.3", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EStED", "NBpA": "2", "MBF": "85.0", "MaxBP": "3.1", "MaxLBP": "4.2", "RM": "0.74", "RV": "-1", "BCN": "8", "BCR": "0.127", "BCD": "0.05", "BCM": "1.9075", "HiPP": "5", "LoPP": "3.5", "Vv": "0.1", "MinCP": "7.7", "MaxCP": "9.0", "CompressorSpeed": "0.0000001", "BVV": "176", "BM": "Cosid", "BCS": "1", "BSA": "0.00", "BRE": "0.95", "TareMaxBP": "1.9"},
"SpringBrake": {"Volume": "20", "MBF": "5.8", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "4.2", "ValveOnArea": "0.0005", "ValveOffArea": "0.0005", "ValvePNBArea": "0.002", "MTC": "4"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.24", "CloseSpeed": "0.2", "DoorMaxShiftL": "0.74", "DoorMaxShiftR": "0.74", "DoorOpenMethod": "Plug", "DoorStayOpen": "5.0", "DoorClosureWarningAuto": "Yes", "DoorBlocked": "Yes", "DoorMaxShiftPlug": "0.05", "DoorCloseDelay": "1.8", "PlatformMaxShift": "0.270", "PlatformSpeed": "0.4", "PlatformOpenMethod": "Shift", "MirrorMaxShift": "45", "DoorNeedPermit": "Yes", "DoorPermitList": "1|0|2|3", "DoorPermitListDefault": "2", "DoorAutoCloseRemote": "Yes"},
"BuffCoupl1": {"CType": "Articulated", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-255"},
"BuffCoupl2": {"CType": "Articulated", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-255"},
"Cntrl": {"BrakeHandle": "MHZ_K5P", "LocBrakeHandle": "FD1", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "74", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "LocalBrake": "PneumaticBrake", "MCPN": "4", "EIMCtrlType": "2", "SCPN": "1", "IniCDelay": "0.0", "SCDelay": "0.8", "CoupledCtrl": "No", "SBD": "0.6", "ASB": "Automatic", "CompressorStart": "Automatic", "ConverterStart": "Automatic", "ConverterStartDelay": "10.15", "BrakeDelays": "PR", "BrakeOpModes": "PN", "ReleaseParkingBySpringBrake": "Yes", "CompartmentLightsStart": "Manual", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "400"}
}

View File

@@ -0,0 +1,52 @@
{
"_source": "pkp/elf_v1/21we-c.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "28650", "Vmax": "160", "PWR": "800", "SandCap": "500", "Type": "ezt", "HeatingP": "37"},
"Load": {"MaxLoad": "95", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2.3", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "20.70", "H": "4.228", "W": "2.96", "Cx": "0.1"},
"Wheels": {"D": "0.850", "AIM": "8000", "Tw": "1.435", "Axle": "Bo'", "Ad": "2.83", "Bd": "16.3", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EStED", "NBpA": "4", "MBF": "85.0", "MaxBP": "3.1", "MaxLBP": "4.2", "RM": "0.74", "RV": "-1", "BCN": "4", "BCR": "0.127", "BCD": "0.05", "BCM": "1.9075", "HiPP": "5", "LoPP": "3.5", "Vv": "0.5", "MinCP": "7.0", "MaxCP": "8.5", "CompressorSpeed": "0.025", "BVV": "150", "BM": "Cosid", "BCS": "1", "BSA": "0.00", "BRE": "0.95", "TareMaxBP": "2.6", "CompressorPower": "Converter", "LPOn": "3.0", "LPOff": "4.5", "UBB1": "2", "UBB2": "8"},
"SpringBrake": {"Volume": "10", "MBF": "2.9", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "4.2", "ValveOnArea": "0.0005", "ValveOffArea": "0.0005", "ValvePNBArea": "0.002", "MTC": "4"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.24", "CloseSpeed": "0.2", "DoorMaxShiftL": "0.74", "DoorMaxShiftR": "0.74", "DoorOpenMethod": "Plug", "DoorStayOpen": "5.0", "DoorClosureWarningAuto": "Yes", "DoorBlocked": "Yes", "DoorMaxShiftPlug": "0.05", "DoorCloseDelay": "1.8", "PlatformMaxShift": "0.270", "PlatformSpeed": "0.4", "PlatformOpenMethod": "Shift", "MirrorMaxShift": "45", "DoorNeedPermit": "Yes", "DoorPermitList": "1|0|2|3", "DoorPermitListDefault": "2", "DoorAutoCloseRemote": "Yes", "MirrorVelClose": "25"},
"BuffCoupl1": {"CType": "Articulated", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-255"},
"BuffCoupl2": {"CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "AutomaticFlag": "39", "ControlType": "en62"},
"Cntrl": {"BrakeHandle": "MHZ_K5P", "LocBrakeHandle": "FD1", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "37", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "LocalBrake": "PneumaticBrake", "MCPN": "4", "EIMCtrlType": "2", "SCPN": "1", "MainInitTime": "10", "IniCDelay": "1.0", "SCDelay": "0.2", "CoupledCtrl": "No", "SBD": "0.6", "ASB": "Yes", "CompressorStart": "Automatic", "ConverterStart": "Automatic", "ConverterStartDelay": "10.0", "PantCompressorStart": "Mixed", "PantAutoValve": "Yes", "BrakeDelays": "PR", "BrakeOpModes": "PN", "DirChangeMaxPos": "3", "HAO": "No", "HMO": "Yes", "HGDP1": "0", "ReleaseParkingBySpringBrake": "Yes", "MotorBlowersStart": "Converter", "CompartmentLightsStart": "Manual", "AutomaticCabActivation": "No", "InactivaCabEmergencyBrake": "Yes", "InactiveCabFlag": "3", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Circuit": {"ImaxHi": "500"},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "2.4", "MaxHoldTime": "0.1", "EmergencyBrakeDelay": "2.1", "CabDependent": "Yes", "RadioStop": "Yes", "EmergencyBrakeWarningSignal": "2", "MagnetLocation": "4.5"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main", "HMaxVoltage": "400"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "1", "MaxVoltage": "4000", "MinV": "2000", "MaxCurrent": "1200", "MinH": "0.38", "MaxH": "2.4", "CSW": "1.4", "PhysicalLayout": "2"},
"Blending": {"MED_amax": "1.04", "MED_EPVC": "No", "MED_Ncor": "No", "MED_FirstDelayED": "2", "MED_ScndDelayED": "1"},
"Engine": {"EngineType": "ElectricInductionMotor", "Trans": "40:218", "dfic": "470", "dfmax": "0.790", "p": "2", "cfu": "33.43", "cim": "10.22", "icif": "0.67", "Uzmax": "2340", "Uzh": "2340", "DU": "20", "I0": "20", "fcfu": "33.5", "F0": "75", "a1": "0.0", "Pmax": "980", "Fh": "48", "Ph": "830", "Vh0": "2", "Vh1": "5", "Imax": "377", "abed": "0", "Flat": "Yes", "edep": "1.0", "MotorBlowersSpeed": "-29", "InvNo": "2", "InvCtrCplFlag": "255"},
"SpeedControl": {"SpeedCtrl": "Yes", "SpeedButtons": "0|40|50|60|70|80|100|120|140|160", "OverrideManual": "Yes", "InitPwr": "0.3", "MaxPwrVel": "5", "StartVel": "2", "VelStep": "5", "PwrStep": "0.1", "MinPwr": "0.3", "MaxPwr": "1.0", "MinVel": "0", "MaxVel": "160", "Offset": "-0.5", "kPpos": "0.1", "kPneg": "0.4", "kIpos": "0.06", "kIneg": "0.06", "BrakeIntervention": "No", "PowerUpSpeed": "0.3", "PowerDownSpeed": "0.3"},
"AI": {"Pantstate": "2"},
"ffList": {"Size": "10", "rows": [
["0","2.49"],
["22.1","2.49"],
["24.1","2.50"],
["31.3","2.50"],
["32.3","2.53"],
["39.3","3.0"],
["65","5.0"],
["91","7.0"],
["124","9.0"],
["161","11.7"],
["177","13.0"]
]},
"LightsList": {"Size": "5", "Wrap": "no", "Default": "1", "rows": [
["0","0"],
["21","34"],
["22","34"],
["34","34"],
["16","1"]
]}
}

View File

@@ -0,0 +1,54 @@
{
"_source": "pkp/elf_v1/22wec-a.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "29000", "Vmax": "120", "PWR": "800", "SandCap": "500", "Type": "ezt", "HeatingP": "37"},
"Load": {"MaxLoad": "108", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2.3", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "21.081", "H": "4.228", "W": "2.96", "Cx": "0.1"},
"Wheels": {"D": "0.850", "AIM": "8000", "Tw": "1.435", "Axle": "Bo'", "Ad": "2.83", "Bd": "16.3", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EStED", "NBpA": "4", "MBF": "85.0", "MaxBP": "3.1", "MaxLBP": "4.2", "RM": "0.74", "RV": "-1", "BCN": "4", "BCR": "0.127", "BCD": "0.05", "BCM": "1.9075", "HiPP": "5", "LoPP": "3.5", "Vv": "0.5", "MinCP": "7.7", "MaxCP": "9.0", "CompressorSpeed": "0.025", "BVV": "150", "BM": "Cosid", "BCS": "1", "BSA": "0.00", "BRE": "0.95", "TareMaxBP": "2.6", "CompressorPower": "Converter", "LPOn": "3.0", "LPOff": "4.5", "UBB1": "2", "UBB2": "8"},
"SpringBrake": {"Volume": "10", "MBF": "2.9", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "4.2", "ValveOnArea": "0.0005", "ValveOffArea": "0.0005", "ValvePNBArea": "0.002", "MTC": "4"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.24", "CloseSpeed": "0.2", "DoorMaxShiftL": "0.74", "DoorMaxShiftR": "0.74", "DoorOpenMethod": "Plug", "DoorStayOpen": "5.0", "DoorClosureWarningAuto": "Yes", "DoorBlocked": "Yes", "DoorMaxShiftPlug": "0.05", "DoorCloseDelay": "1.8", "PlatformMaxShift": "0.270", "PlatformSpeed": "0.4", "PlatformOpenMethod": "Shift", "MirrorMaxShift": "45", "DoorNeedPermit": "Yes", "DoorPermitList": "1|0|2|3", "DoorPermitListDefault": "2", "DoorAutoCloseRemote": "Yes", "MirrorVelClose": "25"},
"BuffCoupl1": {"CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "AutomaticFlag": "39"},
"BuffCoupl2": {"CType": "Articulated", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-255"},
"Cntrl": {"BrakeHandle": "MHZ_K5P", "LocBrakeHandle": "FD1", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "35", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "LocalBrake": "PneumaticBrake", "MCPN": "4", "EIMCtrlType": "2", "SCPN": "1", "MainInitTime": "10", "IniCDelay": "1.0", "SCDelay": "0.2", "CoupledCtrl": "No", "SBD": "0.6", "ASB": "Yes", "CompressorStart": "Automatic", "ConverterStart": "Automatic", "ConverterStartDelay": "10.15", "BrakeDelays": "PR", "BrakeOpModes": "PN", "DirChangeMaxPos": "3", "HAO": "No", "HMO": "Yes", "HGDP1": "0", "ReleaseParkingBySpringBrake": "Yes", "MotorBlowersStart": "Converter", "CompartmentLightsStart": "Manual", "PantEPValveSpring": "No", "PantCompressorStart": "Automatic", "PantAutoValve": "Yes", "AutomaticCabActivation": "No", "InactivaCabEmergencyBrake": "Yes", "InactiveCabFlag": "9", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "2.4", "MaxHoldTime": "0.1", "EmergencyBrakeDelay": "2.1", "CabDependent": "Yes", "RadioStop": "Yes", "EmergencyBrakeWarningSignal": "2", "MagnetLocation": "4.5"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main", "HMaxVoltage": "400"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "1", "MaxVoltage": "4000", "MinV": "2000", "MaxCurrent": "1200", "MinH": "0.38", "MaxH": "2.4", "CSW": "1.4", "PhysicalLayout": "1"},
"Switches": {"PantographPresets": "1|3|2"},
"Circuit": {"ImaxHi": "500"},
"Blending": {"MED_amax": "1.04", "MED_EPVC": "No", "MED_Ncor": "No", "MED_FirstDelayED": "2", "MED_ScndDelayED": "1"},
"Engine": {"EngineType": "ElectricInductionMotor", "Trans": "40:218", "dfic": "470", "dfmax": "0.790", "p": "2", "cfu": "33.43", "cim": "10.22", "icif": "0.67", "Uzmax": "2340", "Uzh": "2340", "DU": "20", "I0": "20", "fcfu": "33.5", "F0": "75", "a1": "0.0", "Pmax": "990", "Fh": "48", "Ph": "830", "Vh0": "2", "Vh1": "5", "Imax": "377", "abed": "0", "Flat": "Yes", "edep": "1.0", "MotorBlowersSpeed": "-29", "InvNo": "2", "InvCtrCplFlag": "255"},
"SpeedControl": {"SpeedCtrl": "Yes", "SpeedButtons": "0|10|20|30|40|50|60|80|100|120", "OverrideManual": "Yes", "InitPwr": "0.3", "MaxPwrVel": "5", "StartVel": "2", "VelStep": "5", "PwrStep": "0.1", "MinPwr": "0.3", "MaxPwr": "1.0", "MinVel": "0", "MaxVel": "160", "Offset": "-0.5", "kPpos": "0.1", "kPneg": "0.4", "kIpos": "0.06", "kIneg": "0.06", "BrakeIntervention": "No", "PowerUpSpeed": "0.3", "PowerDownSpeed": "0.3"},
"AI": {"Pantstate": "1"},
"ffList": {"Size": "3", "rows": [
["0","2.0"],
["33.7","2.08"],
["34.0","2.12"],
["130.4","8.10"]
]},
"ffBrakeList": {"Size": "4", "rows": [
["0","2.0"],
["33.7","2.08"],
["34.8","2.08"],
["34.8","2.17"],
["130.4","8.10"]
]},
"LightsList": {"Size": "6", "Wrap": "no", "Default": "1", "rows": [
["21","34"],
["22","34"],
["16","1"],
["34","34"],
["17","34"],
["38","34"]
]}
}

View File

@@ -0,0 +1,26 @@
{
"_source": "pkp/en57_v1/5bgaiii_led_spot.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "34000", "Vmax": "110", "PWR": "0", "SandCap": "500", "Type": "ezt", "HeatingP": "19.6"},
"Load": {"MaxLoad": "95", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "21.286", "H": "4", "W": "2.9", "Cx": "0.1"},
"Wheels": {"D": "0.92", "AIM": "2000", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.83", "Bd": "13.9", "Rmin": "80", "BearingType": "Roll"},
"BuffCoupl1": {"ControlType": "en57", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "AutomaticFlag": "39"},
"BuffCoupl2": {"ControlType": "en57", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-887", "PowerFlag": "768"},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes", "MagnetLocation": "5"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "3000"},
"Switches": {"Pantograph": "impulse", "Converter": "impulse"},
"Brake": {"BrakeValve": "EP2", "NBpA": "4", "MBF": "85.0", "MaxBP": "4", "BCN": "1", "BCR": "0.203", "BCD": "0.15", "BCM": "9.39", "HiPP": "5", "LoPP": "3.5", "Vv": "0.3", "MinCP": "5.8", "MaxCP": "7.2", "CompressorSpeed": "0.013", "TareMaxBP": "2.5", "BVV": "88", "BM": "P10-Bgu", "BCS": "1.5", "BSA": "5", "BRE": "0.9", "CompressorPower": "Coupler2"},
"Cntrl": {"BrakeHandle": "FVel6", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "52", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "ASB": "Manual", "LocalBrake": "ManualBrake", "MCPN": "3", "SCPN": "3", "IniCDelay": "0.0", "SCDelay": "0.975", "CoupledCtrl": "Yes", "CompartmentLightsStart": "Manual", "BatteryStart": "Disabled", "ConverterStart": "Disabled", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Doors": {"OpenCtrl": "DriverCtrl", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.315", "CloseSpeed": "0.315", "DoorCloseDelay": "1.6", "DoorMaxShiftL": "0.625", "DoorMaxShiftR": "0.625", "DoorOpenMethod": "Shift", "DoorClosureWarning": "Yes", "DoorBlocked": "Yes"}
}

View File

@@ -0,0 +1,26 @@
{
"_source": "pkp/en57_v1/5bgb.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "34000", "Vmax": "110", "PWR": "0", "SandCap": "500", "Type": "ezt", "HeatingP": "19.6"},
"Load": {"MaxLoad": "90", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "21.288", "H": "4", "W": "2.9", "Cx": "0.1"},
"Wheels": {"D": "0.92", "AIM": "2000", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.83", "Bd": "13.9", "Rmin": "80", "BearingType": "Roll"},
"BuffCoupl1": {"ControlType": "en57", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-887", "PowerFlag": "768"},
"BuffCoupl2": {"ControlType": "en57", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "AutomaticFlag": "39"},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes", "MagnetLocation": "5"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "3000"},
"Switches": {"Pantograph": "impulse", "Converter": "impulse"},
"Brake": {"BrakeValve": "EP2", "NBpA": "4", "MBF": "85.0", "MaxBP": "4", "BCN": "1", "BCR": "0.203", "BCD": "0.15", "BCM": "9.39", "HiPP": "5", "LoPP": "3.5", "Vv": "0.3", "MinCP": "5.8", "MaxCP": "7.2", "CompressorSpeed": "0.013", "TareMaxBP": "2.5", "BVV": "88", "BM": "P10-Bgu", "BCS": "1.5", "BSA": "5", "BRE": "0.9", "CompressorPower": "Coupler1"},
"Cntrl": {"BrakeHandle": "FVel6", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "52", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "ASB": "Manual", "LocalBrake": "ManualBrake", "MCPN": "3", "SCPN": "3", "IniCDelay": "0.0", "SCDelay": "0.975", "CoupledCtrl": "Yes", "CompartmentLightsStart": "Manual", "ConverterStart": "Disabled", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Doors": {"OpenCtrl": "DriverCtrl", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.315", "CloseSpeed": "0.315", "DoorCloseDelay": "1.6", "DoorMaxShiftL": "0.625", "DoorMaxShiftR": "0.625", "DoorOpenMethod": "Shift", "DoorClosureWarning": "Yes", "DoorBlocked": "Yes"}
}

View File

@@ -0,0 +1,26 @@
{
"_source": "pkp/en57_v1/6ba_840.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "34000", "Vmax": "110", "PWR": "0", "SandCap": "500", "Type": "ezt", "HeatingP": "19.6"},
"Load": {"MaxLoad": "95", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "21.286", "H": "4", "W": "2.9", "Cx": "0.1"},
"Wheels": {"D": "0.92", "AIM": "2000", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.83", "Bd": "13.9", "Rmin": "80", "BearingType": "Roll"},
"BuffCoupl1": {"ControlType": "en57", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "AutomaticFlag": "39"},
"BuffCoupl2": {"ControlType": "en57", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-887", "PowerFlag": "768"},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes", "MagnetLocation": "5"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "3000"},
"Switches": {"Pantograph": "impulse", "Converter": "impulse"},
"Brake": {"BrakeValve": "EP2", "NBpA": "4", "MBF": "85.0", "MaxBP": "4", "BCN": "1", "BCR": "0.203", "BCD": "0.15", "BCM": "9.39", "HiPP": "5", "LoPP": "3.5", "Vv": "0.3", "MinCP": "5.8", "MaxCP": "7.2", "CompressorSpeed": "0.013", "TareMaxBP": "2.5", "BVV": "88", "BM": "P10-Bgu", "BCS": "1.5", "BSA": "5", "BRE": "0.9", "CompressorPower": "Coupler2"},
"Cntrl": {"BrakeHandle": "FVel6", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "52", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "ASB": "Manual", "LocalBrake": "ManualBrake", "MCPN": "3", "SCPN": "3", "IniCDelay": "0.0", "SCDelay": "0.975", "CoupledCtrl": "Yes", "CompartmentLightsStart": "Manual", "BatteryStart": "Disabled", "ConverterStart": "Disabled", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.32", "CloseSpeed": "0.242", "DoorCloseDelay": "3.35", "DoorMaxShiftL": "0.625", "DoorMaxShiftR": "0.625", "DoorOpenMethod": "Shift", "DoorClosureWarning": "Yes", "DoorClosureWarningAuto": "Yes", "DoorBlocked": "Yes", "DoorNeedPermit": "Yes", "DoorAutoCloseVel": "10.0"}
}

View File

@@ -0,0 +1,25 @@
{
"_source": "pkp/en57_v1/6bai.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "34000", "Vmax": "110", "PWR": "0", "SandCap": "500", "Type": "ezt", "HeatingP": "19.6"},
"Load": {"MaxLoad": "80", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "21.286", "H": "4", "W": "2.9", "Cx": "0.1"},
"Wheels": {"D": "0.92", "AIM": "2000", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.83", "Bd": "13.9", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "K", "NBpA": "4", "MBF": "85.0", "MaxBP": "12", "BCN": "1", "BCR": "0.203", "BCD": "0.15", "BCM": "12.00", "HiPP": "5", "LoPP": "3.5", "Vv": "0.3", "MinCP": "5.8", "MaxCP": "7.2", "CompressorSpeed": "0.013", "TareMaxBP": "2.5", "BVV": "88", "BM": "P10-Bgu", "BCS": "1.5", "BSA": "5", "BRE": "0.9", "CompressorPower": "Coupler2"},
"Doors": {"OpenCtrl": "DriverCtrl", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.315", "CloseSpeed": "0.315", "DoorCloseDelay": "1.6", "DoorMaxShiftL": "0.625", "DoorMaxShiftR": "0.625", "DoorOpenMethod": "Shift", "DoorClosureWarning": "Yes", "DoorBlocked": "Yes"},
"BuffCoupl1": {"CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "AutomaticFlag": "39"},
"BuffCoupl2": {"CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-887", "PowerFlag": "768"},
"Cntrl": {"BrakeHandle": "St113", "BrakeSystem": "ElectroPneumatic", "LocalBrake": "ManualBrake", "MaxBPMass": "52", "BCPN": "5", "BDelay1": "15", "BDelay2": "4", "ASB": "Manual", "DynamicBrake": "Passive", "MCPN": "3", "SCPN": "3", "IniCDelay": "0.0", "SCDelay": "0.975", "CoupledCtrl": "Yes", "CompartmentLightsStart": "Manual", "BatteryStart": "Disabled", "ConverterStart": "Disabled", "rows": [
["-1","0.5","-1","9","Pneumatic"],
["0","-1","0.0","6","ElectroPneumatic"],
["1","-1","0.35","4","ElectroPneumatic"],
["2","-1","-1","0","Pneumatic"],
["3","-1","-1","3","Pneumatic"],
["4","0.34","-1","4","Pneumatic"],
["5","0.0","-1","9","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes", "MagnetLocation": "5"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "3000"},
"Switches": {"Pantograph": "impulse", "Converter": "impulse"}
}

View File

@@ -0,0 +1,40 @@
{
"_source": "pkp/en57akl_v1/akl_ra.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "38000", "Vmax": "120", "PWR": "0", "SandCap": "500", "Type": "ezt", "HeatingP": "20"},
"Load": {"MaxLoad": "80", "LoadQ": "pieces", "LoadAccepted": "Passengers", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "21.286", "H": "4", "W": "2.9", "Cx": "0.1"},
"Wheels": {"D": "0.92", "AIM": "2000", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.7", "Bd": "14.9", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EStED", "NBpA": "4", "MBF": "85.0", "MaxBP": "3.5", "MaxLBP": "3.5", "BCN": "1", "BCR": "0.203", "BCD": "0.105", "BCM": "9.39", "HiPP": "5", "LoPP": "3.5", "Vv": "0.3", "MinCP": "6.2", "MaxCP": "7.2", "CompressorSpeed": "0.01416667", "TareMaxBP": "2.5", "BVV": "107", "BM": "P10-Bgu", "BCS": "1.5", "BSA": "5", "BRE": "0.9", "CompressorPower": "Coupler2", "UBB1": "2", "UBB2": "8", "LPOn": "3.0", "LPOff": "4.5"},
"SpringBrake": {"Volume": "10", "MBF": "29", "MaxSP": "7.2", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "4.2", "ValveOnArea": "0.0001", "ValveOffArea": "0.0001", "ValvePNBArea": "0.002", "MTC": "4"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.2", "CloseSpeed": "0.2", "DoorCloseDelay": "2.5", "DoorMaxShiftL": "0.58", "DoorMaxShiftR": "0.58", "DoorOpenMethod": "Plug", "DoorClosureWarningAuto": "Yes", "DoorBlocked": "Yes", "DoorMaxShiftPlug": "0.035", "DoorNeedPermit": "Yes", "DoorStayOpen": "5.0", "DoorAutoCloseVel": "10.0"},
"BuffCoupl1": {"CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "AutomaticFlag": "39"},
"BuffCoupl2": {"CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-119", "PowerFlag": "1792"},
"Cntrl": {"BrakeHandle": "MHZ_EN57", "LocBrakeHandle": "FD1", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "52", "BCPN": "10", "BDelay1": "15", "BDelay2": "4", "MainInitTime": "10", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "MCPN": "10", "SCPN": "1", "SpeedCtrlATOF": "1", "EIMCtrlAddZeros": "Yes", "IniCDelay": "0.0", "SCDelay": "0.8", "CoupledCtrl": "No", "BrakeOpModes": "PNEPMED", "CompressorStart": "Automatic", "CompartmentLightsStart": "Manual", "ConverterStart": "Disabled", "BatteryStart": "Disabled", "AutomaticCabActivation": "No", "InactivaCabEmergencyBrake": "Yes", "InactiveCabFlag": "15", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Blending": {"MED_Vmax": "95", "MED_Vmin": "1", "MED_Vref": "100", "MED_amax": "0.8", "MED_EPVC": "Yes", "MED_Ncor": "No"},
"SpeedControl": {"SpeedCtrl": "Yes", "OverrideManual": "No", "InitPwr": "1.0", "MaxPwrVel": "-1", "StartVel": "-1", "VelStep": "10", "PwrStep": "0.00", "MinPwr": "1.0", "MaxPwr": "1.0", "MinVel": "0", "MaxVel": "120", "Offset": "-0.1", "kPpos": "0.2", "kPneg": "0.4", "kIpos": "0.00", "kIneg": "0.00", "BrakeIntervention": "No", "SpeedCtrlATOF": "1"},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "2.4", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "2.1", "CabDependent": "Yes", "RadioStop": "Yes", "MagnetLocation": "5"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "3000"},
"Switches": {"Pantograph": "impulse", "Converter": "impulse"},
"AI": {"Pantstate": "2", "IdlePantUp": "Yes"},
"Engine": {"EngineType": "ElectricInductionMotor", "Trans": "46:252", "dfic": "377", "dfmax": "0.98", "p": "2", "cfu": "46.8", "cim": "14.2", "icif": "0.671", "Uzmax": "2340", "Uzh": "2340", "DU": "20", "I0": "20", "fcfu": "49", "F0": "130", "a1": "0", "Pmax": "0", "Fh": "130", "Ph": "1000", "Vh0": "5", "Vh1": "10", "Imax": "557", "abed": "0", "edep": "0.95"},
"LightsList": {"Size": "7", "Wrap": "No", "Default": "1", "rows": [
["0","0"],
["34","34"],
["38","34"],
["22","34"],
["16","1"],
["1","16"],
["21","34"]
]}
}

View File

@@ -0,0 +1,84 @@
{
"_source": "pkp/ep05_v2/ep05.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "82500", "Mred": "12000", "Vmax": "160", "PWR": "2032", "SandCap": "100", "Type": "ep05"},
"Load": {"MaxLoad": "52", "LoadQ": "pieces", "LoadAccepted": "pantstate", "LoadSpeed": "1", "UnLoadSpeed": "0.1"},
"Dimensions": {"L": "16.14", "H": "3.987", "W": "2.950", "Cx": "0.2"},
"Wheels": {"D": "1.250", "AIM": "2000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "3.05", "Bd": "8.55", "Rmin": "120", "BearingType": "Roll"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "85.0", "MaxBP": "3.8", "MaxASBP": "1.3", "RM": "0.56", "BCN": "4", "BCR": "0.1524", "BCD": "0.05", "BCM": "7.13", "Vv": "1.07", "MinCP": "7.5", "MaxCP": "9.0", "BCS": "1.2", "BSA": "0.0", "BM": "P10-Bgu", "BVV": "220", "CompressorSpeed": "0.06", "BRE": "0.85"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "103", "ControlType": "EP05"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Manual", "ManualBrake": "Yes", "LocalBrake": "PneumaticBrake", "DynamicBrake": "Passive", "MCPN": "46", "SCPN": "0", "CoupledCtrl": "Yes", "AutoRelay": "Yes", "SCIM": "1", "IniCDelay": "0.4", "SCDelay": "0.26", "BrakeDelays": "P", "BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "Camshaft": "Yes", "rows": [
["-1","0.60","-1","15","Pneumatic"],
["0","0.50","-1","9","Pneumatic"],
["1","0.46","-1","9","Pneumatic"],
["2","0.42","-1","9","Pneumatic"],
["3","0.38","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.28","-1","9","Pneumatic"],
["6","0.00","-1","15","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "48", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "2", "MaxVoltage": "3600", "MaxCurrent": "1600", "MinH": "0.38", "MinV": "1900", "MaxH": "2.4", "CSW": "1.4", "PhysicalLayout": "3"},
"Engine": {"EngineType": "ElectricSeriesMotor", "Volt": "1500", "Trans": "44:77", "WindingRes": "0.126", "nmax": "1270"},
"Switches": {"PantographPresets": "0|2|3|1"},
"MotorParamTable0": {"rows": [
["0","56.48","80","0.4","370","80","0.445","0"],
["1","45.15","150","0.3","296","150","0.29","0"],
["2","41.45","300","0.2","271","300","0.16","0"],
["3","39.29","400","0.13","257","400","0.13","0"],
["4","38.36","700","0.04","251","700","0.04","0"],
["5","33.83","850","0.01","221","850","0.01","0"]
]},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "800", "ImaxHi": "800", "IminLo": "350", "IminHi": "450"},
"RList": {"Size": "46", "RVent": "Automatic", "RVentnmax": "3300", "RVentCutOff": "0.1", "rows": [
["0","0","0","0","0","0"],
["1","100.000","1","4","0","0"],
["2","14.308","1","4","0","0"],
["3","10.558","1","4","0","0"],
["4","8.908","1","4","0","0"],
["5","7.668","1","4","0","0"],
["6","6.428","1","4","0","0"],
["7","5.744","1","4","0","0"],
["8","5.060","1","4","0","0"],
["9","4.616","1","4","0","0"],
["10","4.172","1","4","0","0"],
["11","3.813","1","4","0","0"],
["12","3.454","1","4","0","0"],
["13","3.100","1","4","0","0"],
["14","2.746","1","4","0","0"],
["15","2.456","1","4","0","0"],
["16","2.215","1","4","0","0"],
["17","1.974","1","4","0","0"],
["18","1.629","1","4","0","0"],
["19","1.284","1","4","0","0"],
["20","1.054","1","4","0","0"],
["21","0.824","1","4","0","0"],
["22","0.593","1","4","0","0"],
["23","0.362","1","4","0","0"],
["24","0.181","1","4","0","0"],
["25","0.000","1","4","0","0"],
["26","0.000","1","4","0","1"],
["27","0.000","1","4","0","2"],
["28","0.000","1","4","0","3"],
["29","0.000","1","4","0","4"],
["30","0.000","1","4","0","4"],
["31","0.000","1","4","0","4"],
["32","0.000","1","4","0","4"],
["33","2.086","2","2","0","0"],
["34","1.727","2","2","0","0"],
["35","1.373","2","2","0","0"],
["36","1.228","2","2","0","0"],
["37","0.987","2","2","0","0"],
["38","0.642","2","2","0","0"],
["39","0.412","2","2","0","0"],
["40","0.181","2","2","0","0"],
["41","0.00","2","2","0","0"],
["42","0.00","2","2","0","1"],
["43","0.00","2","2","0","2"],
["44","0.00","2","2","0","3"],
["45","0.00","2","2","0","4"],
["46","0.00","2","2","0","5"]
]}
}

View File

@@ -0,0 +1,76 @@
{
"_source": "pkp/ep09_v2/104e.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Load": {"MaxLoad": "52", "LoadQ": "pieces", "LoadAccepted": "pantstate", "LoadSpeed": "10", "UnLoadSpeed": "0.1"},
"Param": {"Category": "train", "M": "85400", "Mred": "12000", "Vmax": "160", "PWR": "2920", "SandCap": "730"},
"Dimensions": {"L": "16.74", "H": "4.12", "W": "2.96", "Cx": "0.3"},
"Wheels": {"D": "1.2", "Tw": "1.435", "AIM": "2000", "Axle": "Bo'Bo'", "Ad": "3.2", "Bd": "7.97", "Rmin": "120", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EStED", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.6", "MaxASBP": "1.3", "RM": "0.72", "BCN": "8", "BCR": "0.1143", "BCD": "0.05", "BCM": "5.18", "HiPP": "5", "LoPP": "3.5", "Vv": "0.834", "MinCP": "8.5", "MaxCP": "10.0", "CompressorSpeed": "0.058", "BCS": "1", "BSA": "2", "BM": "P10-Bgu", "BVV": "278", "CompressorPower": "Main"},
"SpringBrake": {"Volume": "10", "MBF": "8", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "3.5", "ValveOnArea": "0.0002", "ValveOffArea": "0.0002", "ValvePNBArea": "0.002", "MTC": "4"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "99"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "27", "BDelay2": "4", "BDelay3": "55", "BDelay4": "25", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "ManualBrake": "No", "DynamicBrake": "Automatic", "DBAM": "1", "MCPN": "31", "SCPN": "5", "AutoRelay": "No", "SCIM": "255", "IniCDelay": "0.5", "SCDelay": "0.25", "SCDDelay": "0.25", "BrakeDelays": "GPR", "BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "rows": [
["-1","0.7","-1","9","Pneumatic"],
["0","0.5","-1","9","Pneumatic"],
["1","0.44","-1","9","Pneumatic"],
["2","0.40","-1","9","Pneumatic"],
["3","0.36","-1","9","Pneumatic"],
["4","0.32","-1","9","Pneumatic"],
["5","0.28","-1","9","Pneumatic"],
["6","0","-1","35","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "3", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "4", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "2", "MaxVoltage": "3600", "MaxCurrent": "2000", "MinH": "0.38", "MinV": "1900", "MaxH": "2.4", "CSW": "1.4", "PhysicalLayout": "3"},
"Engine": {"EngineType": "ElectricSeriesMotor", "Volt": "1500", "Trans": "38:73", "WindingRes": "0.07", "nmax": "1530"},
"Switches": {"Pantograph": "Continuos", "RelayResetButton1": "1", "RelayResetButton2": "4"},
"MotorParamTable0": {"rows": [
["0","52.81","78","0.53","356.8","78","0.53"],
["1","42.73","120","0.42","288.7","120","0.42"],
["2","38.38","193","0.33","259.3","193","0.33"],
["3","36.60","320","0.24","247.3","320","0.24"],
["4","36.66","534","0.16","247.7","534","0.16"],
["5","40.29","1100","0.06","272.2","1100","0.06"]
]},
"Circuit": {"CircuitRes": "0.00", "ImaxLo": "850", "ImaxHi": "850", "IminLo": "520", "IminHi": "650"},
"RList": {"Size": "31", "RVent": "Automatic", "RVentnmax": "3300", "RVentCutOff": "0.1", "rows": [
["0","0","0","0","0","0"],
["1","10.758","1","4","0","2"],
["2","10.758","1","4","0","0"],
["3","7.994","1","4","0","0"],
["4","6.156","1","4","0","0"],
["5","4.808","1","4","0","0"],
["6","4.032","1","4","0","0"],
["7","3.342","1","4","0","0"],
["8","3.014","1","4","0","0"],
["9","2.686","1","4","0","0"],
["10","2.375","1","4","0","0"],
["11","2.064","1","4","0","0"],
["12","1.772","1","4","0","0"],
["13","1.480","1","4","0","0"],
["14","1.196","1","4","0","0"],
["15","0.912","1","4","0","0"],
["16","0.683","1","4","0","0"],
["17","0.454","1","4","0","0"],
["18","0.227","1","4","0","0"],
["19","0","1","4","0","255"],
["20","5.379","2","2","0","0"],
["21","3.997","2","2","0","0"],
["22","3.078","2","2","0","0"],
["23","2.404","2","2","0","0"],
["24","2.016","2","2","0","0"],
["25","1.671","2","2","0","0"],
["26","1.343","2","2","0","0"],
["27","1.032","2","2","0","0"],
["28","0.740","2","2","0","0"],
["29","0.456","2","2","0","0"],
["30","0.227","2","2","0","0"],
["31","0","2","2","0","255"]
]},
"WiperList": {"Size": "4", "Angle": "83", "rows": [
["0","1.0","0.0","0.5"],
["3","1.0","5.0","0.5"],
["3","1.0","0.0","0.5"],
["3","0.5","0.0","0.5"]
]}
}

View File

@@ -0,0 +1,86 @@
{
"_source": "pkp/et22_v3/201e-rn.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "120000", "Mred": "18000", "Vmax": "125", "PWR": "3120", "SandCap": "700", "Type": "et22"},
"Load": {"MaxLoad": "52", "LoadQ": "pieces", "LoadAccepted": "pantstate", "LoadSpeed": "1", "UnLoadSpeed": "0.1"},
"Dimensions": {"L": "19.240", "H": "4.055", "W": "3.005", "Cx": "0.3"},
"Wheels": {"D": "1.250", "AIM": "2000", "Tw": "1.435", "Axle": "Co'Co'", "Ad": "3.05", "Bd": "10.30", "Rmin": "120", "BearingType": "Roll"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.2", "MaxASBP": "1.8", "BCN": "8", "BCR": "0.1143", "BCD": "0.05", "BCM": "7.75", "HiPP": "5", "LoPP": "3.5", "Vv": "1", "MinCP": "7.5", "MaxCP": "8.5", "CompressorSpeed": "0.0566", "BVV": "250", "RM": "0.677", "BSA": "0", "BCS": "1", "BM": "P10-Bgu", "CompressorPower": "Main", "BRE": "0.75"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "99"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "17", "BDelay2": "7", "BDelay3": "45", "BDelay4": "25", "BrakeDelays": "GPR", "BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "ManualBrake": "Yes", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "DynamicBrake": "Passive", "MCPN": "48", "SCPN": "6", "AutoRelay": "No", "IniCDelay": "0.5", "SCDelay": "0.26", "SCDDelay": "0.05", "MotorBlowersStart": "Converter", "MotorBlowersSpeed": "-1310", "rows": [
["-1","0.6","-1","12","Pneumatic"],
["0","0.5","-1","9","Pneumatic"],
["1","0.43","-1","9","Pneumatic"],
["2","0.40","-1","9","Pneumatic"],
["3","0.37","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.28","-1","9","Pneumatic"],
["6","0","-1","18","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "2", "MaxVoltage": "3600", "MaxCurrent": "1800", "MinH": "0.38", "MinV": "1900", "MaxH": "2.4", "CSW": "1.4", "PhysicalLayout": "3"},
"Engine": {"EngineType": "ElectricSeriesMotor", "Volt": "1500", "Trans": "18:79", "WindingRes": "0.14", "nmax": "2390"},
"MotorParamTable0": {"rows": [
["0","21.4","105","0.12","141.5","105","0.12"],
["1","19.57","140","0.07","129.3","140","0.07"],
["2","20.42","190","0.09","134.7","190","0.09"],
["3","22.97","160","0.25","152.1","160","0.25"],
["4","22.12","255","0.16","145.8","255","0.16"],
["5","24.18","170","0.35","158.2","170","0.35"],
["6","22.76","800","0.02","149","800","0.02"]
]},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "600", "ImaxHi": "750", "IminLo": "350", "IminHi": "450"},
"RList": {"Size": "48", "RVent": "Automatic", "RVentnmax": "3300", "RVentCutOff": "0.2", "rows": [
["0","0","0","0","0"],
["1","25.88","1","6","0"],
["2","15.05","1","6","0"],
["3","11.02","1","6","0"],
["4","8.98","1","6","0"],
["5","7.54","1","6","0"],
["6","6.28","1","6","0"],
["7","5.50","1","6","0"],
["8","5.11","1","6","0"],
["9","4.67","1","6","0"],
["10","4.11","1","6","0"],
["11","3.61","1","6","0"],
["12","3.06","1","6","0"],
["13","2.70","1","6","0"],
["14","2.38","1","6","0"],
["15","2.05","1","6","0"],
["16","1.69","1","6","0"],
["17","1.36","1","6","0"],
["18","1.00","1","6","0"],
["19","0.84","1","6","0"],
["20","0.29","1","6","0"],
["21","0.00","1","6","0"],
["22","5.53","2","3","0"],
["23","4.53","2","3","0"],
["24","3.58","2","3","0"],
["25","3.03","2","3","0"],
["26","2.60","2","3","0"],
["27","2.26","2","3","0"],
["28","1.97","2","3","0"],
["29","1.60","2","3","0"],
["30","1.46","2","3","0"],
["31","1.26","2","3","0"],
["32","1.00","2","3","0"],
["33","0.61","2","3","0"],
["34","0.35","2","3","0"],
["35","0.23","2","3","0"],
["36","0.00","2","3","0"],
["37","2.64","3","2","0"],
["38","2.23","3","2","0"],
["39","1.78","3","2","0"],
["40","1.49","3","2","0"],
["41","1.29","3","2","0"],
["42","1.11","3","2","0"],
["43","0.95","3","2","0"],
["44","0.78","3","2","0"],
["45","0.62","3","2","0"],
["46","0.47","3","2","0"],
["47","0.22","3","2","0"],
["48","0.00","3","2","0"]
]}
}

View File

@@ -0,0 +1,86 @@
{
"_source": "pkp/et22_v3/201e-w.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "120000", "Mred": "18000", "Vmax": "125", "PWR": "3120", "SandCap": "700", "Type": "et22"},
"Load": {"MaxLoad": "52", "LoadQ": "pieces", "LoadAccepted": "pantstate", "LoadSpeed": "1", "UnLoadSpeed": "0.1"},
"Dimensions": {"L": "19.240", "H": "4.055", "W": "3.005", "Cx": "0.3"},
"Wheels": {"D": "1.250", "AIM": "2000", "Tw": "1.435", "Axle": "Co'Co'", "Ad": "3.05", "Bd": "10.30", "Rmin": "120", "BearingType": "Roll"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.2", "MaxASBP": "1.8", "BCN": "8", "BCR": "0.1143", "BCD": "0.05", "BCM": "7.75", "HiPP": "5", "LoPP": "3.5", "Vv": "1", "MinCP": "7.5", "MaxCP": "8.5", "CompressorSpeed": "0.0566", "BVV": "250", "RM": "0.677", "BSA": "0", "BCS": "1", "BM": "P10-Bgu", "CompressorPower": "Main", "BRE": "0.75"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "99"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "17", "BDelay2": "7", "BDelay3": "45", "BDelay4": "25", "BrakeDelays": "GPR", "BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "ManualBrake": "Yes", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "DynamicBrake": "Passive", "MCPN": "48", "SCPN": "6", "AutoRelay": "No", "IniCDelay": "0.5", "SCDelay": "0.26", "SCDDelay": "0.05", "MotorBlowersStart": "Converter", "MotorBlowersSpeed": "-1310", "rows": [
["-1","0.6","-1","12","Pneumatic"],
["0","0.5","-1","9","Pneumatic"],
["1","0.43","-1","9","Pneumatic"],
["2","0.40","-1","9","Pneumatic"],
["3","0.37","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.28","-1","9","Pneumatic"],
["6","0","-1","18","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "2", "MaxVoltage": "3600", "MaxCurrent": "1800", "MinH": "0.38", "MinV": "1900", "MaxH": "2.4", "CSW": "1.4", "PhysicalLayout": "3"},
"Engine": {"EngineType": "ElectricSeriesMotor", "Volt": "1500", "Trans": "18:79", "WindingRes": "0.14", "nmax": "2390"},
"MotorParamTable0": {"rows": [
["0","21.4","105","0.12","141.5","105","0.12"],
["1","19.57","140","0.07","129.3","140","0.07"],
["2","20.42","190","0.09","134.7","190","0.09"],
["3","22.97","160","0.25","152.1","160","0.25"],
["4","22.12","255","0.16","145.8","255","0.16"],
["5","24.18","170","0.35","158.2","170","0.35"],
["6","22.76","800","0.02","149","800","0.02"]
]},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "600", "ImaxHi": "750", "IminLo": "350", "IminHi": "450"},
"RList": {"Size": "48", "RVent": "Automatic", "RVentnmax": "3300", "RVentCutOff": "0.2", "rows": [
["0","0","0","0","0"],
["1","25.88","1","6","0"],
["2","15.05","1","6","0"],
["3","11.02","1","6","0"],
["4","8.98","1","6","0"],
["5","7.54","1","6","0"],
["6","6.28","1","6","0"],
["7","5.50","1","6","0"],
["8","5.11","1","6","0"],
["9","4.67","1","6","0"],
["10","4.11","1","6","0"],
["11","3.61","1","6","0"],
["12","3.06","1","6","0"],
["13","2.70","1","6","0"],
["14","2.38","1","6","0"],
["15","2.05","1","6","0"],
["16","1.69","1","6","0"],
["17","1.36","1","6","0"],
["18","1.00","1","6","0"],
["19","0.84","1","6","0"],
["20","0.29","1","6","0"],
["21","0.00","1","6","0"],
["22","5.53","2","3","0"],
["23","4.53","2","3","0"],
["24","3.58","2","3","0"],
["25","3.03","2","3","0"],
["26","2.60","2","3","0"],
["27","2.26","2","3","0"],
["28","1.97","2","3","0"],
["29","1.60","2","3","0"],
["30","1.46","2","3","0"],
["31","1.26","2","3","0"],
["32","1.00","2","3","0"],
["33","0.61","2","3","0"],
["34","0.35","2","3","0"],
["35","0.23","2","3","0"],
["36","0.00","2","3","0"],
["37","2.64","3","2","0"],
["38","2.23","3","2","0"],
["39","1.78","3","2","0"],
["40","1.49","3","2","0"],
["41","1.29","3","2","0"],
["42","1.11","3","2","0"],
["43","0.95","3","2","0"],
["44","0.78","3","2","0"],
["45","0.62","3","2","0"],
["46","0.47","3","2","0"],
["47","0.22","3","2","0"],
["48","0.00","3","2","0"]
]}
}

View File

@@ -0,0 +1,77 @@
{
"_source": "pkp/et42_v2/112e-a.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "82000", "Vmax": "100", "PWR": "2240", "SandCap": "850", "Type": "et42"},
"Load": {"MaxLoad": "52", "LoadQ": "pieces", "LoadAccepted": "pantstate", "LoadSpeed": "1", "UnLoadSpeed": "0.1"},
"Dimensions": {"L": "15.43", "H": "4.62", "W": "2.95", "Cx": "0.3"},
"Wheels": {"D": "1.250", "AIM": "2000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "3.05", "Bd": "8.55", "Rmin": "125", "BearingType": "Roll"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.0", "MaxASBP": "1.5", "BCN": "4", "BCR": "0.127", "BCD": "0.12", "BCM": "5.76", "Vv": "1.2", "MinCP": "7.5", "MaxCP": "9.0", "BCS": "1", "BSA": "0.5", "BM": "P10-Bg", "BVV": "300", "CompressorSpeed": "0.0392", "CompressorPower": "Main", "BRE": "0.7"},
"BuffCoupl1": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "99"},
"BuffCoupl2": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-127"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BrakeDelays": "GP", "BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "ManualBrake": "Yes", "DynamicBrake": "Switch", "MCPN": "38", "SCPN": "5", "AutoRelay": "No", "SCIM": "255", "IniCDelay": "0.5", "SCDelay": "0.1", "rows": [
["-1","0.60","-1","15","Pneumatic"],
["0","0.50","-1","9","Pneumatic"],
["1","0.46","-1","9","Pneumatic"],
["2","0.42","-1","9","Pneumatic"],
["3","0.38","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.28","-1","9","Pneumatic"],
["6","0.00","-1","15","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "1", "MaxVoltage": "3600", "MaxCurrent": "1300", "MinH": "0.42", "MinV": "1900", "MaxH": "1.77", "CSW": "1.4", "PhysicalLayout": "1"},
"Engine": {"EngineType": "ElectricSeriesMotor", "Volt": "1500", "Trans": "25:86", "WindingRes": "0.5", "nmax": "1500"},
"Switches": {"Pantograph": "Continuos", "MotorConnectors": "Toggle"},
"MotorParamTable": {"rows": [
["0","21.86","205","142","150"],
["1","21.49","244","139","187"],
["2","21.48","299","138","234"],
["3","19.77","320","136","297"],
["4","23.75","581","126","333"],
["5","24.47","768","123","412"]
]},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "650", "ImaxHi": "651", "IminLo": "415", "IminHi": "415"},
"RList": {"Size": "38", "RVent": "Automatic", "RVentnmax": "3300", "RVentCutOff": "0.2", "rows": [
["0","0","0","0","0","0"],
["1","10.566","1","4","0","5"],
["2","10.566","1","4","0","3"],
["3","10.566","1","4","0","0"],
["4","9.136","1","4","0","0"],
["5","7.706","1","4","0","0"],
["6","6.881","1","4","0","0"],
["7","6.056","1","4","0","0"],
["8","5.682","1","4","0","0"],
["9","5.308","1","4","0","0"],
["10","4.932","1","4","0","0"],
["11","4.556","1","4","0","0"],
["12","4.211","1","4","0","0"],
["13","3.866","1","4","0","0"],
["14","3.538","1","4","0","0"],
["15","3.21","1","4","0","0"],
["16","2.899","1","4","0","0"],
["17","2.588","1","4","0","0"],
["18","2.296","1","4","0","0"],
["19","2.004","1","4","0","0"],
["20","1.72","1","4","0","0"],
["21","1.436","1","4","0","0"],
["22","1.055","1","4","0","0"],
["23","0.674","1","4","0","0"],
["24","0.337","1","4","0","0"],
["25","0.00","1","4","0","255"],
["26","5.283","2","2","0","0"],
["27","4.568","2","2","0","0"],
["28","3.853","2","2","0","0"],
["29","3.028","2","2","0","0"],
["30","2.654","2","2","0","0"],
["31","2.278","2","2","0","0"],
["32","1.933","2","2","0","0"],
["33","1.605","2","2","0","0"],
["34","1.294","2","2","0","0"],
["35","1.002","2","2","0","0"],
["36","0.718","2","2","0","0"],
["37","0.337","2","2","0","0"],
["38","0.0","2","2","0","255"]
]}
}

View File

@@ -0,0 +1,77 @@
{
"_source": "pkp/et42_v2/112e-b.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "82000", "Vmax": "100", "PWR": "2240", "SandCap": "850", "Type": "et42"},
"Load": {"MaxLoad": "52", "LoadQ": "pieces", "LoadAccepted": "pantstate", "LoadSpeed": "1", "UnLoadSpeed": "0.1"},
"Dimensions": {"L": "15.43", "H": "4.62", "W": "2.95", "Cx": "0.3"},
"Wheels": {"D": "1.250", "AIM": "2000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "3.05", "Bd": "8.55", "Rmin": "125", "BearingType": "Roll"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.0", "MaxASBP": "1.5", "BCN": "4", "BCR": "0.127", "BCD": "0.12", "BCM": "5.76", "Vv": "1.2", "MinCP": "7.5", "MaxCP": "9.0", "BCS": "1", "BSA": "0.5", "BM": "P10-Bg", "BVV": "300", "CompressorSpeed": "0.0392", "CompressorPower": "Main", "BRE": "0.7"},
"BuffCoupl1": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-127"},
"BuffCoupl2": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "99"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BrakeDelays": "GP", "BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "ManualBrake": "Yes", "DynamicBrake": "Switch", "MCPN": "38", "SCPN": "5", "AutoRelay": "No", "SCIM": "255", "IniCDelay": "0.5", "SCDelay": "0.1", "rows": [
["-1","0.60","-1","15","Pneumatic"],
["0","0.50","-1","9","Pneumatic"],
["1","0.46","-1","9","Pneumatic"],
["2","0.42","-1","9","Pneumatic"],
["3","0.38","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.28","-1","9","Pneumatic"],
["6","0.00","-1","15","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "1", "MaxVoltage": "3600", "MaxCurrent": "1300", "MinH": "0.42", "MinV": "1900", "MaxH": "1.77", "CSW": "1.4", "PhysicalLayout": "1"},
"Engine": {"EngineType": "ElectricSeriesMotor", "Volt": "1500", "Trans": "25:86", "WindingRes": "0.5", "nmax": "1500"},
"Switches": {"Pantograph": "Continuos", "MotorConnectors": "Toggle"},
"MotorParamTable": {"rows": [
["0","21.86","205","142","150"],
["1","21.49","244","139","187"],
["2","21.48","299","138","234"],
["3","19.77","320","136","297"],
["4","23.75","581","126","333"],
["5","24.47","768","123","412"]
]},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "650", "ImaxHi": "651", "IminLo": "415", "IminHi": "415"},
"RList": {"Size": "38", "RVent": "Automatic", "RVentnmax": "3300", "RVentCutOff": "0.2", "rows": [
["0","0","0","0","0","0"],
["1","10.566","1","4","0","5"],
["2","10.566","1","4","0","3"],
["3","10.566","1","4","0","0"],
["4","9.136","1","4","0","0"],
["5","7.706","1","4","0","0"],
["6","6.881","1","4","0","0"],
["7","6.056","1","4","0","0"],
["8","5.682","1","4","0","0"],
["9","5.308","1","4","0","0"],
["10","4.932","1","4","0","0"],
["11","4.556","1","4","0","0"],
["12","4.211","1","4","0","0"],
["13","3.866","1","4","0","0"],
["14","3.538","1","4","0","0"],
["15","3.21","1","4","0","0"],
["16","2.899","1","4","0","0"],
["17","2.588","1","4","0","0"],
["18","2.296","1","4","0","0"],
["19","2.004","1","4","0","0"],
["20","1.72","1","4","0","0"],
["21","1.436","1","4","0","0"],
["22","1.055","1","4","0","0"],
["23","0.674","1","4","0","0"],
["24","0.337","1","4","0","0"],
["25","0.00","1","4","0","255"],
["26","5.283","2","2","0","0"],
["27","4.568","2","2","0","0"],
["28","3.853","2","2","0","0"],
["29","3.028","2","2","0","0"],
["30","2.654","2","2","0","0"],
["31","2.278","2","2","0","0"],
["32","1.933","2","2","0","0"],
["33","1.605","2","2","0","0"],
["34","1.294","2","2","0","0"],
["35","1.002","2","2","0","0"],
["36","0.718","2","2","0","0"],
["37","0.337","2","2","0","0"],
["38","0.0","2","2","0","255"]
]}
}

View File

@@ -0,0 +1,67 @@
{
"_source": "pkp/eu04_v1/eu04-01.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "87200", "Mred": "12000", "Vmax": "110", "PWR": "1680", "SandCap": "500", "type": "EU04"},
"Dimensions": {"L": "16.238", "H": "4.343", "W": "2.903", "Cx": "0.3"},
"Wheels": {"D": "1.350", "AIM": "2000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "3.05", "Bd": "8.55", "Rmin": "140", "BearingType": "Roll"},
"Brake": {"BrakeValve": "K", "NBpA": "4", "MBF": "85.0", "MaxBP": "2.8", "RM": "0.43", "LocMaxBP": "3.5", "BCN": "8", "BCR": "0.1524", "BCD": "0.12", "BCM": "8.7", "HiPP": "5", "LoPP": "3.5", "Vv": "0.83", "MinCP": "7.0", "MaxCP": "8.0", "CompressorSpeed": "0.03", "BCS": "0.8", "BSA": "0", "BM": "P10-Bgu", "BVV": "200", "BRE": "0.8"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "103"},
"Cntrl": {"BrakeDelays": "GPRR", "BrakeHandle": "Knorr", "LocBrakeHandle": "Knorr", "BrakeSystem": "Pneumatic", "BCPN": "4", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ManualBrake": "Yes", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "DynamicBrake": "Passive", "MCPN": "31", "SCPN": "8", "AutoRelay": "Optional", "IniCDelay": "0.4", "SCDelay": "0.4", "rows": [
["-1","0.90","0","15","Pneumatic"],
["0","0.50","0","9","Pneumatic"],
["1","-1","-1","0","Pneumatic"],
["2","-1","-1","5","Pneumatic"],
["3","0.001","-1","9","Pneumatic"],
["4","0.00","-1","15","Pneumatic"]
]},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "2", "MaxVoltage": "3600", "MaxCurrent": "1000", "MinH": "0.38", "MaxH": "2.4", "CSW": "1.4", "PhysicalLayout": "3"},
"Engine": {"EngineType": "ElectricSeriesMotor", "Volt": "1500", "Trans": "21:81", "WindingRes": "0.1411", "nmax": "2075"},
"MotorParamTable0": {"rows": [
["0","23.9","200","-0.08","155.9","200","-0.08","1"],
["1","23.28","200","-0.07","151.8","200","-0.07","1"],
["2","22.45","200","-0.06","146.5","200","-0.06","1"],
["3","22.18","200","0","144.7","200","0","1"],
["4","23.09","300","-0.05","150.6","300","-0.05","1"],
["5","22.89","290","-0.02","149.3","290","-0.02","1"],
["6","23.59","360","-0.02","153.9","360","-0.02","1"],
["7","23.95","410","0.01","156.2","410","0.01","1"],
["8","21.94","410","0.05","143.1","410","0.05","0"]
]},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "650", "ImaxHi": "650", "IminLo": "350", "IminHi": "450"},
"RList": {"Size": "31", "RVent": "Automatic", "RVentnmax": "3300", "RVentCutOff": "50", "rows": [
["0","0","0","0","0"],
["1","16.9","1","4","1"],
["2","12.4","1","4","1"],
["3","9.77","1","4","1"],
["4","8.0","1","4","1"],
["5","6.4","1","4","1"],
["6","4.94","1","4","1"],
["7","4.38","1","4","1"],
["8","3.9","1","4","1"],
["9","3.37","1","4","1"],
["10","2.86","1","4","1"],
["11","2.5","1","4","1"],
["12","2.1","1","4","1"],
["13","1.78","1","4","1"],
["14","1.49","1","4","1"],
["15","1.18","1","4","1"],
["16","0.89","1","4","1"],
["17","0.66","1","4","1"],
["18","0.42","1","4","1"],
["19","0.2","1","4","1"],
["20","0.0","1","4","1"],
["21","6.09","2","2","1"],
["22","3.9","2","2","1"],
["23","3.1","2","2","1"],
["24","2.57","2","2","1"],
["25","2.185","2","2","1"],
["26","1.58","2","2","1"],
["27","1.115","2","2","1"],
["28","0.795","2","2","1"],
["29","0.5","2","2","1"],
["30","0.23","2","2","1"],
["31","0.0","2","2","0"]
]}
}

View File

@@ -0,0 +1,20 @@
{
"_source": "pkp/ew58_v1/ew58-d.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "42200", "Mred": "1800", "Vmax": "120", "PWR": "2", "SandCap": "0", "Type": "ezt", "HeatingP": "18"},
"Load": {"MaxLoad": "220", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2", "LoadSpeed": "6", "UnLoadSpeed": "6"},
"Dimensions": {"L": "21.7", "H": "4.12", "W": "2.96", "Cx": "0.1"},
"Wheels": {"D": "0.92", "AIM": "2000", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.75", "Bd": "15.6", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EP1", "NBpA": "4", "MBF": "85.0", "MaxBP": "3.8", "BCN": "4", "BCR": "0.1143", "BCD": "0.08", "BCM": "12.156", "HiPP": "5", "LoPP": "3.5", "Vv": "0.3", "MinCP": "7.0", "MaxCP": "8.0", "CompressorSpeed": "0.013", "TareMaxBP": "2.4", "BVV": "150", "BM": "P10-Bg", "BCS": "1.5", "BSA": "16.00", "BRE": "0.83"},
"Doors": {"OpenCtrl": "DriverCtrl", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.35", "CloseSpeed": "0.35", "DoorCloseDelay": "0.5", "DoorMaxShiftL": "0.53", "DoorMaxShiftR": "0.53", "DoorOpenMethod": "Shift", "DoorClosureWarning": "Yes"},
"BuffCoupl": {"ControlType": "ew58", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-127"},
"Cntrl": {"BrakeSystem": "Pneumatic", "MaxBPMass": "68", "BCPN": "1", "BDelay1": "15", "BDelay2": "4", "ASB": "Manual", "LocalBrake": "ManualBrake", "DynamicBrake": "Passive", "MCPN": "4", "SCPN": "9", "SCIM": "1", "AutoRelay": "Yes", "IniCDelay": "0.0", "SCDelay": "0.8", "CoupledCtrl": "No", "BrakeDelays": "R", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","-1","-1","0","ElectroPneumatic"],
["1","-1","0.35","4","ElectroPneumatic"]
]},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "2", "MaxVoltage": "3600", "MaxCurrent": "1200", "MinH": "0.38", "MaxH": "2.4", "CSW": "1.4", "MinPress": "3.5", "PhysicalLayout": "3"},
"DCEMUED": {"CouplerCheck": "3", "EP_max_Vel": "48"},
"Switches": {"Pantograph": "Continous"}
}

View File

@@ -0,0 +1,68 @@
{
"_source": "pkp/ew58_v1/ew58-sa.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "52400", "Mred": "4980", "Vmax": "120", "PWR": "820", "SandCap": "500", "Type": "ezt", "HeatingP": "21"},
"Load": {"MaxLoad": "220", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2", "LoadSpeed": "6", "UnLoadSpeed": "6"},
"Dimensions": {"L": "21.55", "H": "4", "W": "2.9", "Cx": "0.1"},
"Wheels": {"D": "1.0", "AIM": "2000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "2.83", "Bd": "13.9", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EP1", "NBpA": "4", "MBF": "85.0", "MaxBP": "3.8", "BCN": "4", "BCR": "0.1143", "BCD": "0.08", "BCM": "12.156", "HiPP": "5", "LoPP": "3.5", "TareMaxBP": "2.9", "BVV": "150", "BM": "P10-Bg", "BCS": "1.5", "BSA": "16.00", "BRE": "0.83"},
"Doors": {"OpenCtrl": "DriverCtrl", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.35", "CloseSpeed": "0.35", "DoorCloseDelay": "0.5", "DoorMaxShiftL": "0.53", "DoorMaxShiftR": "0.53", "DoorOpenMethod": "Shift", "DoorClosureWarning": "Yes"},
"BuffCoupl1": {"ControlType": "ew58", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39"},
"BuffCoupl2": {"ControlType": "ew58", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-127"},
"Cntrl": {"BrakeHandle": "FVE408", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "68", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "ASB": "Manual", "LocalBrake": "ManualBrake", "MCPN": "4", "SCPN": "9", "DBAM": "2", "IniCDelay": "0.0", "SCDelay": "0.3", "CoupledCtrl": "No", "SCIM": "1", "AutoRelay": "Yes", "DynamicBrake": "Automatic", "ConverterStart": "Disabled", "BatteryStart": "Disabled", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Security": {"AwareSystem": "CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "0", "MaxVoltage": "3750", "MaxCurrent": "800"},
"Engine": {"EngineType": "ElectricSeriesMotor", "Volt": "750", "Trans": "18:69", "WindingRes": "0.1097", "nmax": "2390"},
"Blending": {"MED_amax": "1.00"},
"DCEMUED": {"CouplerCheck": "2", "EP_min_Im": "80"},
"Switches": {"Pantograph": "Continous", "RelayResetButton1": "101"},
"MotorParamTable0": {"rows": [
["0","11.3","90","0.21","79.5","90","0.21","1"],
["1","12.39","110","0.24","83.9","110","0.24","1"],
["2","11.88","123","0.23","80.5","123","0.23","1"],
["3","11.37","135","0.22","77.2","135","0.22","1"],
["4","11.05","180","0.2","74.6","180","0.2","1"],
["5","10.73","225","0.18","72.1","225","0.18","1"],
["6","10.26","250","0.18","68.7","250","0.18","1"],
["7","9.8","275","0.19","65.4","275","0.19","1"],
["8","9.34","300","0.19","62","300","0.19","1"],
["9","8.15","455","0.1","55.5","455","0.1","0"]
]},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "580", "ImaxHi": "581", "IminLo": "360", "IminHi": "455", "TUHEX_Sum1": "365", "TUHEX_Sum2": "500", "TUHEX_Sum3": "610", "TUHEX_Diff": "10", "TUHEX_MaxIw": "280", "TUHEX_MinIw": "11", "TUHEX_Stages": "3"},
"RList": {"Size": "22", "RVent": "No", "DynBrakeRes1": "4.3", "DynBrakeRes2": "1.956", "rows": [
["0","0","0","0","0","0"],
["1","17.001","1","4","0","0"],
["2","10.677","1","4","1","0"],
["2","7.573","1","4","1","0"],
["2","5.505","1","4","1","0"],
["2","4.477","1","4","1","0"],
["2","3.589","1","4","1","0"],
["2","2.821","1","4","1","0"],
["2","2.157","1","4","1","0"],
["2","1.585","1","4","1","0"],
["2","1.116","1","4","1","0"],
["2","0.688","1","4","1","0"],
["2","0.320","1","4","1","0"],
["2","0.000","1","4","1","0"],
["3","0.000","1","4","1","1"],
["3","0.000","1","4","1","2"],
["3","0.000","1","4","1","3"],
["3","0.000","1","4","1","4"],
["3","0.000","1","4","1","5"],
["4","0.000","1","4","1","6"],
["4","0.000","1","4","1","7"],
["4","0.000","1","4","1","8"],
["4","0.000","1","4","1","9"]
]}
}

View File

@@ -0,0 +1,68 @@
{
"_source": "pkp/ew58_v1/ew58-sb.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "52400", "Mred": "4980", "Vmax": "120", "PWR": "820", "SandCap": "500", "Type": "ezt", "HeatingP": "21"},
"Load": {"MaxLoad": "220", "LoadQ": "pieces", "LoadAccepted": "Passengers,Pantstate", "OverLoadFactor": "2", "LoadSpeed": "6", "UnLoadSpeed": "6"},
"Dimensions": {"L": "21.55", "H": "4", "W": "2.9", "Cx": "0.1"},
"Wheels": {"D": "1.0", "AIM": "2000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "2.83", "Bd": "13.9", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EP1", "NBpA": "4", "MBF": "85.0", "MaxBP": "3.8", "BCN": "4", "BCR": "0.1143", "BCD": "0.08", "BCM": "12.156", "HiPP": "5", "LoPP": "3.5", "TareMaxBP": "2.5", "BVV": "150", "BM": "P10-Bg", "BCS": "1.5", "BSA": "16.00", "BRE": "0.83"},
"Doors": {"OpenCtrl": "DriverCtrl", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.35", "CloseSpeed": "0.35", "DoorCloseDelay": "0.5", "DoorMaxShiftL": "0.53", "DoorMaxShiftR": "0.53", "DoorOpenMethod": "Shift", "DoorClosureWarning": "Yes"},
"BuffCoupl1": {"CControlType": "ew58", "Type": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "-127"},
"BuffCoupl2": {"ControlType": "ew58", "CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39"},
"Cntrl": {"BrakeHandle": "FVE408", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "68", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "ASB": "Manual", "LocalBrake": "ManualBrake", "MCPN": "4", "SCPN": "9", "DBAM": "2", "IniCDelay": "0.0", "SCDelay": "0.3", "CoupledCtrl": "No", "SCIM": "1", "AutoRelay": "Yes", "DynamicBrake": "Automatic", "ConverterStart": "Disabled", "BatteryStart": "Disabled", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Security": {"AwareSystem": "CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Clima": {"Heating": "Main"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "0", "MaxVoltage": "3750", "MaxCurrent": "800"},
"Engine": {"EngineType": "ElectricSeriesMotor", "Volt": "750", "Trans": "18:69", "WindingRes": "0.1097", "nmax": "2390"},
"Blending": {"MED_amax": "1.00"},
"DCEMUED": {"CouplerCheck": "1", "EP_min_Im": "80"},
"Switches": {"Pantograph": "Continous", "RelayResetButton1": "101"},
"MotorParamTable0": {"rows": [
["0","11.3","90","0.21","79.5","90","0.21","1"],
["1","12.39","110","0.24","83.9","110","0.24","1"],
["2","11.88","123","0.23","80.5","123","0.23","1"],
["3","11.37","135","0.22","77.2","135","0.22","1"],
["4","11.05","180","0.2","74.6","180","0.2","1"],
["5","10.73","225","0.18","72.1","225","0.18","1"],
["6","10.26","250","0.18","68.7","250","0.18","1"],
["7","9.8","275","0.19","65.4","275","0.19","1"],
["8","9.34","300","0.19","62","300","0.19","1"],
["9","8.15","455","0.1","55.5","455","0.1","0"]
]},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "580", "ImaxHi": "581", "IminLo": "360", "IminHi": "455", "TUHEX_Sum1": "365", "TUHEX_Sum2": "500", "TUHEX_Sum3": "610", "TUHEX_Diff": "10", "TUHEX_MaxIw": "280", "TUHEX_MinIw": "11", "TUHEX_Stages": "3"},
"RList": {"Size": "22", "RVent": "No", "DynBrakeRes1": "4.3", "DynBrakeRes2": "1.956", "rows": [
["0","0","0","0","0","0"],
["1","17.001","1","4","0","0"],
["2","10.677","1","4","1","0"],
["2","7.573","1","4","1","0"],
["2","5.505","1","4","1","0"],
["2","4.477","1","4","1","0"],
["2","3.589","1","4","1","0"],
["2","2.821","1","4","1","0"],
["2","2.157","1","4","1","0"],
["2","1.585","1","4","1","0"],
["2","1.116","1","4","1","0"],
["2","0.688","1","4","1","0"],
["2","0.320","1","4","1","0"],
["2","0.000","1","4","1","0"],
["3","0.000","1","4","1","1"],
["3","0.000","1","4","1","2"],
["3","0.000","1","4","1","3"],
["3","0.000","1","4","1","4"],
["3","0.000","1","4","1","5"],
["4","0.000","1","4","1","6"],
["4","0.000","1","4","1","7"],
["4","0.000","1","4","1","8"],
["4","0.000","1","4","1","9"]
]}
}

View File

@@ -0,0 +1,50 @@
{
"_source": "pkp/impuls_v1/31we-a_pr.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "31800", "Vmax": "160", "PWR": "1000", "SandCap": "200", "Type": "ezt"},
"Load": {"MaxLoad": "42", "LoadQ": "pieces", "LoadAccepted": "Passengers", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "1"},
"Dimensions": {"L": "21.06", "H": "4", "W": "2.9", "Cx": "0.1"},
"Wheels": {"D": "0.840", "AIM": "2000", "Tw": "1.435", "Axle": "Bo'", "Ad": "2.83", "Bd": "13.9", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EStED", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.37", "MaxLBP": "4.37", "BCN": "4", "BCR": "0.127", "BCD": "0.05", "BCM": "1.9075", "HiPP": "5", "LoPP": "3.5", "Vv": "0.125", "MinCP": "7.5", "MaxCP": "9.0", "MinCP_B": "8.5", "MaxCP_B": "10.0", "CompressorSpeed": "0.00625", "BVV": "125", "BM": "Cosid", "BCS": "1", "BSA": "0.00", "BRE": "0.95", "TareMaxBP": "3.85", "CompressorPower": "Converter", "LPOn": "2.8", "LPOff": "4.3", "UBB1": "2", "EmergencyCutsOffHandle": "Yes"},
"SpringBrake": {"Volume": "10", "MBF": "2.9", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "4.2", "ValveOnArea": "0.0005", "ValveOffArea": "0.0005", "ValvePNBArea": "0.002", "MTC": "4"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.21", "CloseSpeed": "0.19", "DoorCloseDelay": "2.1", "DoorOpenWithPermit": "1", "DoorMaxShiftL": "0.7", "DoorMaxShiftR": "0.7", "DoorOpenMethod": "Plug", "DoorMaxShiftPlug": "0.028", "DoorStayOpen": "5.0", "DoorClosureWarningAuto": "Yes", "DoorBlocked": "Yes", "DoorAutoCloseRemote": "Yes", "PlatformMaxShift": "0.270", "PlatformSpeed": "0.4", "PlatformOpenMethod": "Shift", "MirrorMaxShift": "45", "DoorNeedPermit": "Yes", "MirrorVelClose": "20", "DoorsPermitLightBlinking": "2"},
"BuffCoupl1": {"CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "AutomaticFlag": "39", "ControlType": "31we_pr"},
"BuffCoupl2": {"CType": "Articulated", "AllowedFlag": "-255", "ControlType": "impuls"},
"Cntrl": {"BrakeHandle": "MHZ_6P", "LocBrakeHandle": "FD1", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "37.8", "BCPN": "4", "BDelay1": "15", "BDelay2": "4", "LocalBrake": "PneumaticBrake", "MCPN": "10", "SCPN": "1", "IniCDelay": "0.0", "SCDelay": "0.8", "CoupledCtrl": "No", "EIMCtrlAddZeros": "Yes", "SBD": "0.65", "ASB": "Yes", "ConverterStart": "Automatic", "ConverterStartDelay": "8", "MainInitTime": "2", "BrakeOpModes": "PN", "MotorBlowersStart": "Converter", "EIMCtrlEmergency": "Yes", "PantEPValveStart": "Automatic", "PantEPValveSpring": "No", "SpringBrakeCutsOffDrive": "No", "SpringBrakeDriveEmergencyVel": "2", "ReleaseParkingBySpringBrakeWhenDoorIsOpen": "Yes", "CompartmentLightsStart": "Manual", "HAO": "Yes", "OMP": "0.8", "AutomaticCabActivation": "No", "InactivaCabEmergencyBrake": "Yes", "InactiveCabFlag": "239", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "2.4", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "2.1", "CabDependent": "Yes", "RadioStop": "Yes", "MagnetLocation": "4.5"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "24", "LMaxCurrent": "300"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "0", "MaxVoltage": "3900", "MinV": "2100", "MaxCurrent": "1200", "MinH": "0.38", "MaxH": "2.4", "CSW": "1.4"},
"Engine": {"EngineType": "ElectricInductionMotor", "InvNo": "2", "Trans": "60:252", "dfic": "794", "dfmax": "1.500", "p": "2", "cfu": "40.1", "cim": "12.24", "icif": "0.679", "Uzmax": "2200", "Uzh": "1500", "DU": "20", "I0": "10", "fcfu": "47.6", "fcfuh": "31.4", "F0": "100", "a1": "0", "Pmax": "1260", "Fh": "48", "Ph": "800", "Vh0": "5", "Vh1": "15", "Imax": "475", "abed": "0", "edep": "3.0", "eimclf": "Yes", "MotorBlowersSpeed": "-15", "MotorBlowersSustainTime": "300", "MotorBlowersStartVelocity": "0.1"},
"SpeedControl": {"SpeedCtrl": "Yes", "SpeedButtons": "10|20|40|60|80|100|120|140|160", "OverrideManual": "No", "InitPwr": "1.0", "MaxPwrVel": "-1", "StartVel": "-1", "VelStep": "10", "PwrStep": "0.0", "MinPwr": "0.2", "MaxPwr": "1.0", "MinVel": "0", "MaxVel": "160", "Offset": "-0.5", "kPpos": "0.2", "kPneg": "0.4", "kIpos": "0.00", "kIneg": "0.00", "BrakeIntervention": "No", "SpeedCtrlATOF": "2"},
"AI": {"Pantstate": "2", "LocalBrakeAccFactor": "0.5", "IdlePantUp": "No"},
"Blending": {"MED_amax": "1.08", "MED_EPVC": "No", "MED_Ncor": "No", "MED_FrEDFactor": "0", "MED_FirstDelayED": "0.5", "MED_ScndDelayED": "0.25"},
"Switches": {"PantographPresets": "0|1|2|3"},
"Circuit": {"ImaxHi": "500"},
"LightsList": {"Size": "8", "Wrap": "No", "Default": "1", "rows": [
["0","0"],
["388","34"],
["21","34"],
["1","16"],
["16","1"],
["22","34"],
["38","38"],
["34","34"]
]},
"ffList": {"Size": "5", "rows": [
["0","0.5"],
["24","1.0"],
["35","1.0"],
["42","1.3"],
["42","1.3"],
["74","2.3"]
]}
}

View File

@@ -0,0 +1,20 @@
{
"_source": "pkp/impuls_v1/31we-b_pr.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "38700", "Vmax": "160", "PWR": "2", "SandCap": "0", "Type": "ezt"},
"Load": {"MaxLoad": "48", "LoadQ": "pieces", "LoadAccepted": "Passengers", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "15.92", "H": "4.12", "W": "2.96", "Cx": "0.1"},
"Wheels": {"D": "0.84", "AIM": "500", "Tw": "1.435", "Axle": "2'1'", "Ad": "2.75", "Bd": "15.92", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EStED", "NBpA": "2", "MBF": "85.0", "MaxBP": "4.06", "MaxLBP": "4.06", "BCN": "6", "BCR": "0.127", "BCD": "0.05", "BCM": "1.9075", "HiPP": "5", "LoPP": "3.5", "Vv": "0.01", "MinCP": "10.0", "MaxCP": "12.0", "CompressorSpeed": "0.0000001", "BVV": "187", "BM": "Cosid", "BCS": "1", "BSA": "0.00", "BRE": "0.95", "TareMaxBP": "3.09", "CompressorPower": "Converter"},
"SpringBrake": {"Volume": "10", "MBF": "2.9", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.8", "PressOn": "4.5", "ValveOnArea": "0.0005", "ValveOffArea": "0.0005", "ValvePNBArea": "0.002", "MTC": "4"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.21", "CloseSpeed": "0.19", "DoorCloseDelay": "2.1", "DoorOpenWithPermit": "1", "DoorMaxShiftL": "0.7", "DoorMaxShiftR": "0.7", "DoorOpenMethod": "Plug", "DoorMaxShiftPlug": "0.028", "DoorStayOpen": "5.0", "DoorClosureWarningAuto": "Yes", "DoorBlocked": "Yes", "DoorAutoCloseRemote": "Yes", "PlatformMaxShift": "0.270", "PlatformSpeed": "0.4", "PlatformOpenMethod": "Shift", "MirrorMaxShift": "45", "DoorNeedPermit": "Yes"},
"BuffCoupl": {"CType": "Articulated", "AllowedFlag": "-255", "ControlType": "impuls"},
"Cntrl": {"BrakeSystem": "Pneumatic", "LocBrakeHandle": "FD1", "MaxBPMass": "53", "BCPN": "2", "BDelay1": "15", "BDelay2": "4", "LocalBrake": "PneumaticBrake", "DynamicBrake": "Passive", "MCPN": "10", "SCPN": "0", "AutoRelay": "Yes", "IniCDelay": "0.0", "SCDelay": "0.8", "CoupledCtrl": "Yes", "ASB": "Yes", "CompartmentLightsStart": "Manual", "HAO": "Yes", "OMP": "0.8", "PantEPValveStart": "Automatic", "PantEPValveSpring": "No", "PantCompressorStart": "Automatic", "PantAutoValve": "Yes", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","-1","-1","0","ElectroPneumatic"],
["1","-1","0.35","4","ElectroPneumatic"],
["2","-1","-1","0","ElectroPneumatic"]
]},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "24", "LMaxCurrent": "300"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "1", "MaxVoltage": "3900", "MinV": "2100", "MaxCurrent": "1200", "MinH": "0.38", "MaxH": "2.4", "CSW": "1.4", "PhysicalLayout": "1"}
}

View File

@@ -0,0 +1,20 @@
{
"_source": "pkp/impuls_v1/31we-c_pr.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "38700", "Vmax": "160", "PWR": "2", "SandCap": "0", "Type": "ezt"},
"Load": {"MaxLoad": "46", "LoadQ": "pieces", "LoadAccepted": "Passengers", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "15.92", "H": "4.12", "W": "2.96", "Cx": "0.1"},
"Wheels": {"D": "0.84", "AIM": "500", "Tw": "1.435", "Axle": "1'2'", "Ad": "2.75", "Bd": "15.92", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EStED", "NBpA": "2", "MBF": "85.0", "MaxBP": "4.06", "MaxLBP": "4.06", "BCN": "6", "BCR": "0.127", "BCD": "0.05", "BCM": "1.9075", "HiPP": "5", "LoPP": "3.5", "Vv": "0.01", "MinCP": "10.0", "MaxCP": "12.0", "CompressorSpeed": "0.0000001", "BVV": "187", "BM": "Cosid", "BCS": "1", "BSA": "0.00", "BRE": "0.95", "TareMaxBP": "3.09", "CompressorPower": "Converter"},
"SpringBrake": {"Volume": "10", "MBF": "2.9", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.8", "PressOn": "4.5", "ValveOnArea": "0.0005", "ValveOffArea": "0.0005", "ValvePNBArea": "0.002", "MTC": "4"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.21", "CloseSpeed": "0.19", "DoorCloseDelay": "2.1", "DoorOpenWithPermit": "1", "DoorMaxShiftL": "0.7", "DoorMaxShiftR": "0.7", "DoorOpenMethod": "Plug", "DoorMaxShiftPlug": "0.028", "DoorStayOpen": "5.0", "DoorClosureWarningAuto": "Yes", "DoorBlocked": "Yes", "DoorAutoCloseRemote": "Yes", "PlatformMaxShift": "0.270", "PlatformSpeed": "0.4", "PlatformOpenMethod": "Shift", "MirrorMaxShift": "45", "DoorNeedPermit": "Yes"},
"BuffCoupl": {"CType": "Articulated", "AllowedFlag": "-255", "ControlType": "impuls"},
"Cntrl": {"BrakeSystem": "Pneumatic", "LocBrakeHandle": "FD1", "MaxBPMass": "53", "BCPN": "2", "BDelay1": "15", "BDelay2": "4", "LocalBrake": "PneumaticBrake", "DynamicBrake": "Passive", "MCPN": "10", "SCPN": "0", "AutoRelay": "Yes", "IniCDelay": "0.0", "SCDelay": "0.8", "CoupledCtrl": "Yes", "ASB": "Yes", "CompartmentLightsStart": "Manual", "HAO": "Yes", "OMP": "0.8", "PantEPValveStart": "Automatic", "PantEPValveSpring": "No", "PantCompressorStart": "Automatic", "PantAutoValve": "Yes", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","-1","-1","0","ElectroPneumatic"],
["1","-1","0.35","4","ElectroPneumatic"],
["2","-1","-1","0","ElectroPneumatic"]
]},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "24", "LMaxCurrent": "300"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "1", "MaxVoltage": "3900", "MinV": "2100", "MaxCurrent": "1200", "MinH": "0.38", "MaxH": "2.4", "CSW": "1.4", "PhysicalLayout": "2"}
}

View File

@@ -0,0 +1,50 @@
{
"_source": "pkp/impuls_v1/31we-d_pr.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "31800", "Vmax": "160", "PWR": "1000", "SandCap": "200", "Type": "ezt"},
"Load": {"MaxLoad": "54", "LoadQ": "pieces", "LoadAccepted": "Passengers", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "1"},
"Dimensions": {"L": "21.06", "H": "4", "W": "2.9", "Cx": "0.1"},
"Wheels": {"D": "0.840", "AIM": "2000", "Tw": "1.435", "Axle": "Bo'", "Ad": "2.83", "Bd": "13.9", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EStED", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.37", "MaxLBP": "4.37", "BCN": "4", "BCR": "0.127", "BCD": "0.05", "BCM": "1.9075", "HiPP": "5", "LoPP": "3.5", "Vv": "0.125", "MinCP": "8.5", "MaxCP": "10.0", "MinCP_B": "7.5", "MaxCP_B": "9.0", "CompressorSpeed": "0.00625", "BVV": "125", "BM": "Cosid", "BCS": "1", "BSA": "0.00", "BRE": "0.95", "TareMaxBP": "3.85", "CompressorPower": "Converter", "LPOn": "2.8", "LPOff": "4.3", "UBB1": "2", "EmergencyCutsOffHandle": "Yes"},
"SpringBrake": {"Volume": "10", "MBF": "2.9", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "4.2", "ValveOnArea": "0.0005", "ValveOffArea": "0.0005", "ValvePNBArea": "0.002", "MTC": "4"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.21", "CloseSpeed": "0.19", "DoorCloseDelay": "2.1", "DoorOpenWithPermit": "1", "DoorMaxShiftL": "0.7", "DoorMaxShiftR": "0.7", "DoorOpenMethod": "Plug", "DoorMaxShiftPlug": "0.028", "DoorStayOpen": "5.0", "DoorClosureWarningAuto": "Yes", "DoorBlocked": "Yes", "DoorAutoCloseRemote": "Yes", "PlatformMaxShift": "0.270", "PlatformSpeed": "0.4", "PlatformOpenMethod": "Shift", "MirrorMaxShift": "45", "DoorNeedPermit": "Yes", "MirrorVelClose": "20", "DoorsPermitLightBlinking": "2"},
"BuffCoupl1": {"CType": "Articulated", "AllowedFlag": "-255", "ControlType": "impuls"},
"BuffCoupl2": {"CType": "Automatic", "kB": "4200", "DmaxB": "0.05", "FmaxB": "1740", "kC": "6800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "AutomaticFlag": "39", "ControlType": "31we_pr"},
"Cntrl": {"BrakeHandle": "MHZ_6P", "LocBrakeHandle": "FD1", "BrakeSystem": "ElectroPneumatic", "MaxBPMass": "37.8", "BCPN": "4", "BDelay1": "15", "BDelay2": "4", "LocalBrake": "PneumaticBrake", "MCPN": "10", "SCPN": "1", "IniCDelay": "0.0", "SCDelay": "0.8", "CoupledCtrl": "No", "EIMCtrlAddZeros": "Yes", "SBD": "0.65", "ASB": "Yes", "ConverterStart": "Automatic", "ConverterStartDelay": "8", "MainInitTime": "2", "BrakeOpModes": "PN", "MotorBlowersStart": "Converter", "EIMCtrlEmergency": "Yes", "PantEPValveStart": "Automatic", "PantEPValveSpring": "No", "SpringBrakeCutsOffDrive": "No", "SpringBrakeDriveEmergencyVel": "2", "ReleaseParkingBySpringBrakeWhenDoorIsOpen": "Yes", "CompartmentLightsStart": "Manual", "HAO": "Yes", "OMP": "0.8", "AutomaticCabActivation": "No", "InactivaCabEmergencyBrake": "Yes", "InactiveCabFlag": "239", "rows": [
["-1","0.5","0.0","6","ElectroPneumatic"],
["0","0.5","-1","4","ElectroPneumatic"],
["1","0.5","0.35","4","ElectroPneumatic"],
["2","0.5","0.35","4","Pneumatic"],
["3","0.5","0.35","4","Pneumatic"],
["4","-1","0.35","4","Pneumatic"],
["5","0.34","-1","4","Pneumatic"],
["6","-1","-1","0","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "2.4", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "2.1", "CabDependent": "Yes", "RadioStop": "Yes", "MagnetLocation": "4.5"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "24", "LMaxCurrent": "300"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "0", "MaxVoltage": "3900", "MinV": "2100", "MaxCurrent": "1200", "MinH": "0.38", "MaxH": "2.4", "CSW": "1.4"},
"Engine": {"EngineType": "ElectricInductionMotor", "InvNo": "2", "Trans": "60:252", "dfic": "794", "dfmax": "1.500", "p": "2", "cfu": "40.1", "cim": "12.24", "icif": "0.679", "Uzmax": "2200", "Uzh": "1500", "DU": "20", "I0": "10", "fcfu": "47.6", "fcfuh": "31.4", "F0": "100", "a1": "0", "Pmax": "1260", "Fh": "48", "Ph": "800", "Vh0": "5", "Vh1": "15", "Imax": "475", "abed": "0", "edep": "3.0", "eimclf": "Yes", "MotorBlowersSpeed": "-15", "MotorBlowersSustainTime": "300", "MotorBlowersStartVelocity": "0.1"},
"SpeedControl": {"SpeedCtrl": "Yes", "SpeedButtons": "10|20|40|60|80|100|120|140|160", "OverrideManual": "No", "InitPwr": "1.0", "MaxPwrVel": "-1", "StartVel": "-1", "VelStep": "10", "PwrStep": "0.0", "MinPwr": "0.2", "MaxPwr": "1.0", "MinVel": "0", "MaxVel": "160", "Offset": "-0.5", "kPpos": "0.2", "kPneg": "0.4", "kIpos": "0.00", "kIneg": "0.00", "BrakeIntervention": "No", "SpeedCtrlATOF": "2"},
"AI": {"Pantstate": "1", "LocalBrakeAccFactor": "0.5", "IdlePantUp": "No"},
"Blending": {"MED_amax": "1.08", "MED_EPVC": "No", "MED_Ncor": "No", "MED_FrEDFactor": "0", "MED_FirstDelayED": "0.5", "MED_ScndDelayED": "0.25"},
"Switches": {"PantographPresets": "0|1|2|3"},
"Circuit": {"ImaxHi": "500"},
"LightsList": {"Size": "8", "Wrap": "No", "Default": "1", "rows": [
["0","0"],
["388","34"],
["21","34"],
["1","16"],
["16","1"],
["22","34"],
["38","38"],
["34","34"]
]},
"ffList": {"Size": "5", "rows": [
["0","0.5"],
["24","1.0"],
["35","1.0"],
["42","1.3"],
["42","1.3"],
["74","2.3"]
]}
}

View File

@@ -0,0 +1,36 @@
{
"_source": "road/man/nl293.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "road", "M": "12000", "Vmax": "90", "PWR": "206"},
"Load": {"LoadAccepted": "Passengers,Luggage", "MaxLoad": "44", "LoadQ": "pieces", "OverLoadFactor": "1.2", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "10.5", "H": "3", "W": "2.6", "Cx": "0.4"},
"Wheels": {"D": "1.0", "AIM": "300", "Tw": "2.3", "Axle": "1'A", "Bd": "4.615", "Rmin": "10.5"},
"Brake": {"BrakeType": "Hydraulic", "NBpA": "2", "MBF": "14.75", "MaxBP": "0.5", "BCN": "4", "BCR": "0.2", "BCD": "0.2", "BCM": "2.0"},
"Doors": {"OpenCtrl": "DriverCtrl", "CloseCtrl": "DriverCtrl", "OpenSpeed": "30", "CloseSpeed": "38", "DoorMaxShiftL": "88", "DoorMaxShiftR": "88", "DoorOpenMethod": "Fold", "DoorClosureWarning": "Yes"},
"BuffCoupl": {"CType": "Bare"},
"Cntrl": {"BrakeSystem": "Individual", "BCPN": "10", "LocalBrake": "HydraulicBrake", "MCPN": "10", "SCPN": "5", "IniCDelay": "1.0", "SCDelay": "0.25", "AutoRelay": "Yes", "OilStart": "Automatic", "FuelStart": "Automatic", "rows": []},
"Light": {"Light": "Accu", "LMaxVoltage": "12", "LCap": "340", "LRS": "Generator", "LMaxCurrent": "10"},
"Power": {"EnginePower": "Internal"},
"Engine": {"EngineType": "DieselEngine", "Trans": "10:54", "nmin": "600", "nmax": "2000", "nmax_cutoff": "1900", "AIM": "50", "EUS": "2.5", "EDS": "2.5", "IsTC": "Yes", "TC_TMMax": "1.52", "TC_CP": "0.85", "TC_LT": "1700", "TC_LR": "2.0", "TC_ULR": "2.0", "TC_FRI": "1", "TC_FRD": "1", "TC_TII": "1.63", "TC_TIO": "0", "TC_TOO": "0", "TC_LS": "33", "TC_ULS": "25.4", "IsRetarder": "Yes", "R_Place": "2", "R_TII": "2.0", "R_MT": "1000", "R_MP": "100", "R_FRI": "1", "R_FRD": "1", "R_MinVel": "30"},
"MotorParamTable": {"minVelfullengage": "2.0", "engageDia": "1", "engageMaxForce": "1600", "engagefriction": "1", "rows": [
["0","0","0","-1","1"],
["1","2.81","-1","18.8","1"],
["2","1.84","15.2","28.6","1"],
["3","1.36","25.4","38.8","1"],
["4","1.00","34.6","52.7","1"],
["5","0.80","43.2","200","1"]
]},
"DList": {"Size": "10", "nMmax": "20.83", "Mmax": "1400", "Mnmax": "1050", "nmax": "31.67", "nominalfill": "1", "Mstand": "150", "rows": [
["0","0.1","0"],
["1","0.1","2"],
["2","0.2","2"],
["3","0.3","2"],
["4","0.4","2"],
["5","0.5","2"],
["6","0.6","2"],
["7","0.7","2"],
["8","0.8","2"],
["9","0.9","2"],
["10","1.0","2"]
]}
}

View File

@@ -0,0 +1,94 @@
{
"_source": "pkp/sa134_v1/218mc_a.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "41000", "Vmax": "120", "PWR": "382", "SandCap": "100", "Type": "dmu"},
"Load": {"MaxLoad": "50", "OverLoadFactor": "2.2", "LoadQ": "pieces", "LoadAccepted": "passengers", "LoadSpeed": "0.5", "UnLoadSpeed": "0.7"},
"Dimensions": {"L": "20.75", "H": "4.2", "W": "2.9", "Cx": "0.0"},
"Wheels": {"D": "0.84", "Tw": "1.435", "AIM": "300", "Axle": "B'1'", "Ad": "2", "Bd": "17.23", "Rmin": "75", "BearingType": "Roll"},
"Brake": {"BrakeValve": "EStED", "Size": "150", "NBpA": "4", "MBF": "85.0", "MaxBP": "2.9", "LocMaxBP": "3.8", "RV": "-1", "RM": "0.77", "BCN": "6", "BCR": "0.127", "BCD": "0.1", "BCM": "2.15", "HiPP": "5", "LoPP": "3.5", "Vv": "0.50", "MinCP": "7.0", "MaxCP": "8.0", "CompressorSpeed": "0.0208", "CompressorPower": "Main", "BCS": "0.5", "BSA": "0", "BM": "Cosid", "BVV": "150", "BRE": "0.97", "TareMaxBP": "2.2", "LPOn": "3.0", "LPOff": "4.5", "UBB1": "2", "UBB2": "8"},
"SpringBrake": {"Volume": "15", "MBF": "4.35", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "0.5", "ValveOnArea": "0.0005", "ValveOffArea": "0.0005", "ValvePNBArea": "0.002", "MTC": "4"},
"Doors": {"OpenCtrl": "Mixed", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.23", "CloseSpeed": "0.19", "DoorCloseDelay": "2.45", "DoorMaxShiftL": "0.78", "DoorMaxShiftR": "0.78", "DoorOpenMethod": "Plug", "DoorClosureWarning": "No", "DoorBlocked": "Yes", "DoorMaxShiftPlug": "0.04", "MirrorMaxShift": "75", "DoorStayOpen": "10", "DoorNeedPermit": "Yes", "DoorPermitList": "1|0|2|3", "DoorPermitListDefault": "2", "DoorAutoCloseRemote": "No"},
"BuffCoupl1": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "ControlType": "218M"},
"BuffCoupl2": {"CType": "Articulated", "AllowedFlag": "-55", "ControlType": "218M"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "3", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Yes", "LocalBrake": "PneumaticBrake", "ManualBrake": "No", "MCPN": "5", "SCPN": "1", "AutoRelay": "Yes", "IniCDelay": "1.0", "SCDelay": "0.3", "BrakeDelays": "PR", "MaxBPMass": "55", "BrakeHandle": "MHZ_K5P", "LocBrakeHandle": "FD1", "WaterStart": "Battery", "OilStart": "Automatic", "FuelStart": "Automatic", "ConverterStart": "Automatic", "ConverterStartDelay": "4", "BrakeOpModes": "PN", "EIMCtrlType": "3", "DirChangeMaxPos": "4", "SBD": "0.47", "AutomaticCabActivation": "No", "InactiveCabFlag": "27", "CompartmentLightsStart": "Manual", "rows": [
["-1","0.6","-1","20","Pneumatic"],
["0","0.5","-1","2","Pneumatic"],
["1","0.46","-1","2","Pneumatic"],
["2","0.42","-1","2","Pneumatic"],
["3","0.38","-1","2","Pneumatic"],
["4","0.34","-1","2","Pneumatic"],
["5","0.29","-1","2","Pneumatic"],
["6","0","-1","40","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "2.4", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "2.1", "RadioStop": "Yes", "CabDependent": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "24", "LMaxCurrent": "155"},
"Engine": {"EngineType": "DieselEngine", "Trans": "69:229", "TransEff": "0.90", "nmin": "850", "nmax": "2100", "nmax_cutoff": "2150", "nmin_hdrive": "1000", "nmin_hdrive_factor": "800", "nmin_retarder": "1400", "MaxVelANS": "255", "AIM": "150", "EUS": "2.5", "EDS": "2.5", "IsTC": "Yes", "TC_TMMax": "4.23", "TC_CP": "0.80", "TC_LT": "3000", "TC_LR": "1", "TC_ULR": "1", "TC_FRI": "0.25", "TC_FRD": "1", "TC_TII": "1.97", "TC_TIO": "0", "TC_TOO": "0", "TC_LS": "80", "TC_ULS": "75", "IsRetarder": "Yes", "R_Place": "0", "R_TII": "18.0", "R_MT": "2000", "R_MP": "300", "R_FRI": "1", "R_FRD": "1", "R_MinVel": "16", "R_EngageVel": "95", "WaterMaxTemperature": "105", "WaterCoolingTemperature": "95", "WaterFlowTemperature": "75", "WaterAuxCircuit": "No", "WaterCoolingFanSpeed": "-100", "NominalCoolingPower": "300"},
"Clima": {"HMaxVoltage": "3000", "Heating": "Generator", "HGeneratorEngine": "Main", "HGeneratorMinRPM": "696", "HGeneratorMinVoltage": "2400", "HGeneratorMaxRPM": "1500", "HGeneratorMaxVoltage": "3300"},
"SpeedControl": {"SpeedCtrl": "Yes", "SpeedButtons": "0|30|40|50|60|70|80|90|100|120", "OverrideManual": "Yes", "InitPwr": "0.3", "MaxPwrVel": "5", "StartVel": "2", "VelStep": "5", "PwrStep": "0.1", "MinPwr": "0.3", "MaxPwr": "1.0", "MinVel": "0", "MaxVel": "120", "Offset": "-0.5", "kPpos": "0.1", "kPneg": "0.4", "kIpos": "0.06", "kIneg": "0.06", "BrakeIntervention": "Yes"},
"TurboPos": {"TurboPos": "1"},
"MotorParamTable": {"minVelfullengage": "-0.1", "engageDia": "1", "engageMaxForce": "3300", "engagefriction": "5.0", "rows": [
["0","0","0","-1","1"],
["1","0.72","-1","200","1"]
]},
"DList": {"Size": "10", "Mmax": "2750", "nMmax": "18.3", "nmax": "33.3", "Mnmax": "2142", "nominalfill": "1.0", "Mstand": "250.0", "NomFuelConsRate": "220", "rows": [
["0","0.15","0"],
["1","0.10","2"],
["2","0.20","2"],
["3","0.30","2"],
["4","0.40","2"],
["5","0.50","2"],
["6","0.60","2"],
["7","0.70","2"],
["8","0.80","2"],
["9","0.90","2"],
["10","1.00","2"]
]},
"DMList": {"rows": [
["0","0"],
["80","591"],
["400","738"],
["800","1477"],
["1300","2400"],
["1500","2400"],
["1750","2071"],
["2000","1824"],
["2200","1508"]
]},
"HTCList": {"rows": [
["0.000","4.89"],
["0.061","4.44"],
["0.159","3.63"],
["0.236","3.12"],
["0.293","2.81"],
["0.354","2.46"],
["0.427","2.14"],
["0.492","1.87"],
["0.565","1.62"],
["0.642","1.39"],
["0.720","1.23"],
["0.800","1.00"],
["1.000","0.00"]
]},
"UCList": {"Size": "5", "IntegratedBrakePN": "Yes", "IntegratedBrake": "Yes", "rows": [
["0","3","0","0","0","0.15","0.15","0","1","0"],
["1","2","0","0","0","0.15","0.15","1","3","0"],
["2","1","-1","0","-1","0.15","0.15","3","3","1"],
["3","1","-1","1","0","0.00","0.15","3","4","1"],
["4","0","-1","1","0","0.15","0.00","4","5","3"],
["5","0","0","1","1","0.15","0.15","4","5","3"]
]},
"LightsList": {"Size": "5", "Wrap": "no", "Default": "1", "rows": [
["0","0"],
["21","34"],
["22","34"],
["34","34"],
["16","1"]
]},
"WiperList": {"Size": "5", "Angle": "-75", "Default": "1", "rows": [
["3","0.5","0.5","0.0"],
["0","0.8","0.0","0.5"],
["3","0.8","6.0","0.5"],
["3","0.8","3.0","0.0"],
["3","0.8","1.0","0.0"]
]}
}

View File

@@ -0,0 +1,13 @@
{
"_source": "pkp/sieciowiec_v1/a.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "35500", "Vmax": "80", "HeatingP": "20", "LightP": "0.56"},
"Load": {"MaxLoad": "80", "LoadQ": "pieces", "LoadAccepted": "Passengers,Luggage", "OverLoadFactor": "2", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "21.13", "H": "4", "W": "2.9", "Cx": "0.1"},
"Wheels": {"D": "1.00", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.83", "Bd": "13.9", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeType": "None", "NBpA": "0", "MBF": "0", "MaxBP": "0"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.11", "FmaxB": "320", "kC": "4500", "DmaxC": "0.1", "FmaxC": "850", "beta": "0.58", "AllowedFlag": "115"},
"Cntrl": {"MCPN": "1", "BrakeSystem": "Individual", "LocalBrake": "NoBrake", "rows": []},
"Light": {"Light": "Generator", "LGeneratorEngine": "WheelsDriven", "LMaxVoltage": "24", "AlterLight": "Accu", "AlterLMaxVoltage": "24", "AlterLCap": "495", "AlterLRS": "Generator"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "3000", "AlterHeating": "PowerCable", "AlterHPowerTrans": "SteamPower", "AlterHSteamPress": "0.45"}
}

View File

@@ -0,0 +1,41 @@
{
"_source": "pkp/sm03_v1/sm03.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "23200", "Vmax": "25.68", "PWR": "111.855", "SandCap": "200"},
"Dimensions": {"L": "6.94", "H": "3.21", "W": "2.97", "Cx": "0.3"},
"Wheels": {"D": "0.94", "Tw": "1.435", "AIM": "250", "Axle": "B", "Ad": "2.5", "Bd": "2.5", "Rmin": "50", "BearingType": "Roll"},
"Brake": {"BrakeValve": "K", "NBpA": "2", "MBF": "85.0", "MaxBP": "3.5", "LocMaxBP": "5.0", "BCN": "1", "BCR": "0.114", "BCD": "0.12", "BCM": "10.0", "HiPP": "5", "LoPP": "3.5", "Vv": "0.3", "MinCP": "7.0", "MaxCP": "8.0", "CompressorSpeed": "0.0125", "CompressorPower": "Main", "BCS": "0.8", "BSA": "0", "BM": "P10-Bg", "BVV": "40", "BRE": "0.9"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "3"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "4", "BDelay1": "15", "BDelay2": "4", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "ManualBrake": "Yes", "DynamicBrake": "Passive", "MCPN": "9", "SCPN": "7", "AutoRelay": "No", "IniCDelay": "0.5", "SCDelay": "0.1", "BrakeDelays": "P", "BrakeHandle": "Knorr", "LocBrakeHandle": "Knorr", "FuelStart": "Automatic", "ConverterStart": "Automatic", "rows": [
["-1","0.6","0","15","Pneumatic"],
["0","0.5","0","9","Pneumatic"],
["1","-1","-1","0","Pneumatic"],
["2","-1","-1","5","Pneumatic"],
["3","0.34","-1","9","Pneumatic"],
["4","0.0","-1","15","Pneumatic"]
]},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "24", "LMaxCurrent": "100"},
"Engine": {"EngineType": "DieselEngine", "Trans": "1:1", "nmin": "900", "nmax": "1500", "nmax_cutoff": "1550", "AIM": "100", "OilMinPressure": "0.343", "OilMaxPressure": "0.686", "WaterCoolingTemperature": "80", "WaterShutters": "yes", "WaterAuxCircuit": "no", "HeatKFO": "160", "HeatKFO2": "180", "HeatKFS": "160", "HeatKFE": "7", "HeatKW": "2", "HeatKV": "0.3", "IsTC": "No", "TC_TMMax": "1.00", "TC_CP": "0.9", "TC_LT": "1000", "TC_LR": "1", "TC_ULR": "1", "TC_FRI": "1", "TC_FRD": "1", "TC_TII": "0.8", "TC_TIO": "0", "TC_TOO": "0", "TC_LS": "3.5", "TC_ULS": "3.0", "EUS": "0.35", "EDS": "1.0"},
"MotorParamTable": {"minVelfullengage": "3.8", "engageDia": "1", "engageMaxForce": "800", "engagefriction": "1", "rows": [
["0","0","0","-1"],
["1","46.88","-1","5.73"],
["2","0","0","-1"],
["3","28.65","2.9","9.42"],
["4","0","0","-1"],
["5","17.12","4.7","15.69"],
["6","0","0","-1"],
["7","10.46","10.6","100"]
]},
"DList": {"Size": "9", "Mmax": "784.5", "nMmax": "15", "Mnmax": "686.4", "nmax": "25", "nominalfill": "1", "Mstand": "200.0", "rows": [
["0","0.1","0"],
["1","0.2","2"],
["2","0.3","2"],
["3","0.4","2"],
["4","0.5","2"],
["5","0.6","2"],
["6","0.7","2"],
["7","0.8","2"],
["8","0.9","2"],
["9","1.0","2"]
]}
}

View File

@@ -0,0 +1,43 @@
{
"_source": "pkp/sm31_v1/411d.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "116400", "Mred": "15000", "Vmax": "80", "PWR": "880", "SandCap": "400"},
"Dimensions": {"L": "17.00", "H": "4.292", "W": "2.904", "Cx": "0.3"},
"Wheels": {"D": "1.100", "AIM": "4000", "Tw": "1.435", "Axle": "Co'Co'", "Ad": "2.6", "Bd": "13.1", "Rmin": "80"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.3", "LocMaxBP": "4.3", "MaxASBP": "1.3", "BCN": "12", "BCR": "0.114", "BCD": "0.06", "BCM": "5.84", "HiPP": "5", "LoPP": "3.5", "Vv": "0.8", "MinCP": "7.5", "MaxCP": "8.0", "CompressorSpeed": "0.03", "BCS": "1", "BSA": "2", "BM": "P10-Bgu", "CompressorPower": "Converter", "BVV": "130", "BRE": "0.9"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "35"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "MCPN": "11", "SCPN": "2", "AutoRelay": "Yes", "IniCDelay": "0.5", "SCDelay": "0.1", "BrakeDelays": "GP", "BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "ConverterStart": "Automatic", "ConverterStartDelay": "7", "rows": [
["-1","0.60","-1","15","Pneumatic"],
["0","0.50","-1","9","Pneumatic"],
["1","0.46","-1","9","Pneumatic"],
["2","0.42","-1","9","Pneumatic"],
["3","0.38","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.28","-1","9","Pneumatic"],
["6","0.00","-1","15","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "100"},
"Engine": {"AIM": "1.6", "EngineType": "DumbDE", "Trans": "19:70", "Ftmax": "392000", "Vhyp": "4", "Vadd": "5", "Cr": "1", "RelayType": "1"},
"TurboPos": {"TurboPos": "5"},
"MotorParamTable": {"rows": [
["0","17.567","64","1500","15","30","0"],
["1","16.29","123.7","1750","32","43","0"],
["2","15","183.3","2000","49","200","0"]
]},
"Circuit": {"ImaxLo": "2800", "IminLo": "2000"},
"WWList": {"Size": "11", "rows": [
["496","0","0","0"],
["496","30","870","3000"],
["496","75","870","3000"],
["496","105","870","3000"],
["496","135","870","3000"],
["568","213","870","3000"],
["640","305","870","3000"],
["712","395","870","3000"],
["784","495","870","3000"],
["856","595","870","3000"],
["928","705","870","3000"],
["1000","800","870","3000"]
]}
}

View File

@@ -0,0 +1,48 @@
{
"_source": "pkp/sm40_v1/sm40.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "62000", "Mred": "10000", "Vmax": "80", "PWR": "1175", "SandCap": "400"},
"Dimensions": {"L": "11.30", "H": "4.44", "W": "3.05", "Cx": "0.3"},
"Wheels": {"D": "1.04", "AIM": "4000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "1.75", "Bd": "10.3", "Rmin": "100"},
"Brake": {"BrakeValve": "K", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.0", "LocMaxBP": "5.0", "BCN": "4", "BCR": "0.236", "BCD": "0.1", "BCM": "6.54", "HiPP": "5", "LoPP": "3.5", "Vv": "0.50", "MinCP": "7.0", "MaxCP": "8.0", "CompressorSpeed": "0.0208", "CompressorPower": "Main", "BCS": "0.8", "BSA": "0", "BM": "P10-Bg", "BVV": "127", "BRE": "0.75"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "850", "beta": "0.5", "AllowedFlag": "39"},
"Cntrl": {"ControlType": "sm40", "BrakeSystem": "Pneumatic", "BCPN": "4", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "0", "LocalBrake": "PneumaticBrake", "ManualBrake": "Yes", "MCPN": "20", "SCPN": "1", "AutoRelay": "Yes", "IniCDelay": "0.4", "SCDelay": "0.4", "BrakeDelays": "GP", "BrakeHandle": "Knorr", "LocBrakeHandle": "Knorr", "ConverterStart": "Automatic", "rows": [
["-1","0.90","0","15","Pneumatic"],
["0","0.50","0","9","Pneumatic"],
["1","-1","-1","0","Pneumatic"],
["2","-1","-1","5","Pneumatic"],
["3","0.001","-1","9","Pneumatic"],
["4","0.00","-1","15","Pneumatic"]
]},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "100"},
"Engine": {"EngineType": "DieselElectric", "Trans": "17:77", "Ftmax": "184500", "Flat": "1", "Vhyp": "5.5", "Vadd": "5.5", "Cr": "1.0", "RelayType": "41"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "3000"},
"MotorParamTable": {"rows": [
["0","5.84","137","300","91","25","0"],
["1","3.32","191","400","230","0","2700"]
]},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "3350", "ImaxHi": "3350", "IminLo": "400", "IminHi": "400"},
"WWList": {"Size": "20", "rows": [
["470","0","0","0"],
["900","0","0","0"],
["900","0","0","0"],
["800","75","360","2600"],
["800","100","405","2600"],
["800","125","450","2600"],
["800","145","450","2600"],
["845","180","495","2600"],
["890","230","540","2600"],
["930","270","585","2600"],
["970","315","630","2600"],
["1005","355","675","2600"],
["1100","360","410","2600"],
["1100","365","285","2600"],
["1100","370","405","2600"],
["1100","375","440","2600"],
["1100","380","468","2600"],
["1100","385","490","2600"],
["1100","390","502","2600"],
["1100","400","512","2600"],
["1100","412","540","2600"]
]}
}

View File

@@ -0,0 +1,42 @@
{
"_source": "pkp/sm42_v1/6d1.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "74000", "Mred": "10000", "Vmax": "90", "PWR": "590", "SandCap": "300"},
"Dimensions": {"L": "14.24", "H": "4.285", "W": "3.170", "Cx": "0.2"},
"Wheels": {"D": "1.100", "AIM": "4000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "2.6", "Bd": "7.524", "Rmin": "80"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.0", "MaxASBP": "1.8", "BCN": "4", "BCR": "0.1143", "BCD": "0.05", "BCM": "6.68", "HiPP": "5.0", "LoPP": "3.5", "Vv": "0.8", "MinCP": "7.0", "MaxCP": "8.0", "CompressorSpeed": "0.057", "CompressorPower": "Engine", "BVV": "200", "BRE": "0.85"},
"BuffCoupl": {"ControlType": "sm42", "CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "103"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "3", "BDelay3": "36", "BDelay4": "22", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "MCPN": "11", "SCPN": "1", "AutoRelay": "No", "IniCDelay": "0.5", "SCDelay": "0.1", "BrakeDelays": "GP", "BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "ManualBrake": "Yes", "OilStart": "Mixed", "WaterStart": "Battery", "ConverterStart": "Automatic", "rows": [
["-1","0.7","-1","15","Pneumatic"],
["0","0.5","-1","9","Pneumatic"],
["1","0.45","-1","9","Pneumatic"],
["2","0.41","-1","9","Pneumatic"],
["3","0.38","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.28","-1","9","Pneumatic"],
["6","0","-1","15","Pneumatic"]
]},
"Security": {"AwareSystem": "Active", "AwareMinSpeed": "0.0", "AwareDelay": "60", "SoundSignalDelay": "2.5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "2.5", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "91"},
"Engine": {"AIM": "1.6", "RPMDecRate": "4.0", "EngineType": "DumbDE", "Trans": "17:75", "Ftmax": "228000", "Vhyp": "7", "Vadd": "5.5", "Cr": "1", "ShuntMode": "1", "WaterCoolingTemperature": "81", "WaterCoolingFanSpeed": "-1200", "WaterMaxTemperature": "85", "OilMinPressure": "0.15", "OilMinTemperature": "25", "OilMaxTemperature": "90", "HeaterMinTemperature": "55", "HeaterMaxTemperature": "60", "HeatKFS": "35", "HeatKFO": "30", "HeatKFE": "7", "HeatKW": "0.011", "HeatKV": "0.3", "HeatKFO2": "100"},
"TurboPos": {"TurboPos": "5"},
"MotorParamTable": {"rows": [
["0","17.567","64","1500","15","0","0"],
["1","15","183.3","2000","49","0","0"]
]},
"Circuit": {"CircuitRes": "0.1", "ImaxLo": "1900", "ImaxHi": "1900", "IminLo": "400", "IminHi": "400"},
"WWList": {"Size": "11", "rows": [
["496","0","0","0","0","0","0"],
["496","20","125","600","0","140","180"],
["496","50","156","846","0","140","180"],
["496","90","194","1065","0","140","180"],
["496","135","240","1260","0","140","180"],
["568","182","293","1428","25","150","200"],
["640","234","353","1571","50","160","235"],
["712","286","420","1688","80","200","270"],
["784","348","494","1780","100","235","305"],
["856","408","575","1845","130","300","370"],
["928","474","664","1886","150","360","410"],
["1000","540","760","1900","170","450","490"]
]}
}

View File

@@ -0,0 +1,42 @@
{
"_source": "pkp/sn61_v2/sn61.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "Type": "sn61", "M": "54810", "Vmax": "100", "PWR": "368", "SandCap": "100"},
"Load": {"MaxLoad": "50", "LoadQ": "pieces", "LoadAccepted": "passengers", "LoadSpeed": "1", "UnLoadSpeed": "1"},
"Dimensions": {"L": "24.22", "H": "4.12", "W": "2.96", "Cx": "0.5"},
"Wheels": {"D": "0.94", "Tw": "1.435", "AIM": "300", "Axle": "B'2'", "Ad": "3.2", "Bd": "18.36", "Rmin": "100", "BearingType": "Roll"},
"Brake": {"BrakeValve": "ESt3d_PZZ", "Size": "150", "NBpA": "4", "MBF": "85.0", "MaxBP": "3.8", "LocMaxBP": "3.8", "BCN": "3", "BCR": "0.127", "BCD": "0.1", "BCM": "6.54", "HiPP": "5", "LoPP": "3.5", "Vv": "0.50", "MinCP": "7.0", "MaxCP": "8.0", "CompressorSpeed": "0.0208", "CompressorPower": "Main", "BCS": "1.5", "BSA": "2", "BM": "P10-Bg", "BVV": "150", "BRE": "0.85"},
"Doors": {"OpenCtrl": "Passenger", "CloseCtrl": "Passenger", "OpenSpeed": "110", "CloseSpeed": "400", "DoorMaxShiftL": "89", "DoorMaxShiftR": "89", "DoorOpenMethod": "Roll", "DoorClosureWarning": "No"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "ControlType": "sn61"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Manual", "DirChangeMaxPos": "2", "LocalBrake": "PneumaticBrake", "ManualBrake": "Yes", "MCPN": "8", "SCPN": "5", "AutoRelay": "Yes", "IniCDelay": "1.5", "SCDelay": "0.3", "BrakeDelays": "P", "BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "FuelStart": "Automatic", "WaterStart": "Battery", "OilStart": "Automatic", "ConverterStart": "Automatic", "rows": [
["-1","0.6","-1","20","Pneumatic"],
["0","0.5","-1","2","Pneumatic"],
["1","0.46","-1","2","Pneumatic"],
["2","0.42","-1","2","Pneumatic"],
["3","0.38","-1","2","Pneumatic"],
["4","0.34","-1","2","Pneumatic"],
["5","0.29","-1","2","Pneumatic"],
["6","0","-1","40","Pneumatic"]
]},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Engine": {"EngineType": "DieselEngine", "Trans": "39:83", "nmin": "660", "nmax": "1350", "nmax_cutoff": "1250", "AIM": "150"},
"MotorParamTable": {"minVelfullengage": "7.8", "engageDia": "1", "engageMaxForce": "3800", "engagefriction": "1", "rows": [
["0","0","0","-1","1"],
["1","5.056","-1","20.3","1"],
["2","3.325","14.7","30.8","1"],
["3","2.259","25.4","45.3","1"],
["4","1.486","40.9","68.9","1"],
["5","1.000","62.4","200","1"]
]},
"DList": {"Size": "8", "Mmax": "3630", "nMmax": "13.333", "Mnmax": "3100", "nmax": "21", "nominalfill": "0.9", "Mstand": "300.0", "rows": [
["0","0","0"],
["1","0.4","0"],
["2","0.1","0"],
["3","0.6","1"],
["4","0.15","3"],
["5","0.40","3"],
["6","0.60","3"],
["7","0.80","3"],
["8","1.00","4"]
]}
}

View File

@@ -0,0 +1,56 @@
{
"_source": "pkp/st44_v2/m62-1151.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "116000", "Mred": "20000", "Vmax": "100", "PWR": "1232", "SandCap": "400", "Type": "st44"},
"Dimensions": {"L": "17.55", "H": "4.62", "W": "2.95", "Cx": "0.1"},
"Wheels": {"D": "1.05", "AIM": "4000", "Tw": "1.435", "Axle": "Co'Co'", "Ad": "2.6", "Bd": "8.6", "Rmin": "80"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "124", "MaxBP": "4.1", "MaxLBP": "3.9", "MaxASBP": "1.8", "BCN": "8", "BCR": "0.1016", "BCD": "0.1", "BCM": "10.4", "HiPP": "5", "LoPP": "3.5", "Vv": "0.888", "MinCP": "7", "MaxCP": "8.5", "CompressorSpeed": "0.077", "BCS": "1", "BSA": "2", "BM": "P10-Bgu", "BVV": "222", "CompressorPower": "Main", "BRE": "0.9", "UBB1": "1", "UBB2": "2"},
"BuffCoupl": {"ControlType": "kolomna", "CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "MCPN": "15", "SCPN": "2", "IniCDelay": "0.5", "SCDelay": "0.1", "BrakeDelays": "GP", "BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "AutoRelay": "Yes", "ManualBrake": "Yes", "OilStart": "Automatic", "WaterStart": "Battery", "ConverterStart": "Automatic", "rows": [
["-1","0.7","-1","15","Pneumatic"],
["0","0.5","-1","9","Pneumatic"],
["1","0.46","-1","9","Pneumatic"],
["2","0.42","-1","9","Pneumatic"],
["3","0.38","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.29","-1","9","Pneumatic"],
["6","0","-1","15","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "100"},
"Clima": {"HMaxVoltage": "3000", "Heating": "Generator", "HGeneratorEngine": "Main", "HGeneratorMinRPM": "400", "HGeneratorMinVoltage": "2400", "HGeneratorMaxRPM": "750", "HGeneratorMaxVoltage": "3300"},
"Engine": {"AIM": "3.3", "EngineType": "DieselElectric", "Trans": "15:68", "Ftmax": "396000", "Flat": "1", "Vhyp": "18", "Vadd": "0", "Cr": "1.107", "WaterFlowTemperature": "80", "WaterCoolingTemperature": "45", "WaterMaxTemperature": "95", "WaterShutters": "Yes", "WaterAuxCircuit": "Yes", "WaterAuxCoolingTemperature": "60", "WaterAuxMaxTemperature": "75", "WaterAuxShutters": "Yes", "OilMinPressure": "0.25", "OilMaxPressure": "0.8", "OilMaxTemperature": "71", "HeatKFO": "40", "HeatKFO2": "190", "HeatKW": "0.2", "WaterCoolingFanSpeed": "1.86"},
"LightsList": {"Size": "8", "Wrap": "No", "Default": "4", "rows": [
["38","38","/","A"],
["38","38","/","A"],
["34","34"],
["0","0"],
["21","34"],
["1","16"],
["16","1"],
["22","34"]
]},
"MotorParamTable": {"rows": [
["0","10","179","2000","72","2450","0"],
["1","8","190","1600","95","2250","3600"],
["2","7","300","1400","170","0","3450"]
]},
"WWList": {"Size": "15", "rows": [
["400","0","0","0"],
["400","40","180","1500"],
["425","100","240","2000"],
["450","250","300","2080"],
["475","450","330","3660"],
["500","500","360","3830"],
["525","550","380","4000"],
["550","600","405","4160"],
["575","700","430","4330"],
["600","750","450","4660"],
["625","800","475","4830"],
["650","850","500","5160"],
["675","950","525","5330"],
["700","1050","550","5500"],
["725","1150","575","5660"],
["750","1232","600","6000"]
]}
}

View File

@@ -0,0 +1,55 @@
{
"_source": "pkp/st45_v2/301dd.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "97000", "Mred": "15000", "Vmax": "120", "PWR": "1330", "SandCap": "400"},
"Dimensions": {"L": "18.99", "H": "4.243", "W": "3.034", "Cx": "0.3"},
"Wheels": {"D": "1.100", "AIM": "4000", "Tw": "1.435", "Axle": "Co'Co'", "Ad": "1.950", "Bd": "10.3", "Rmin": "100"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.1", "MaxASBP": "1.5", "RM": "0.71", "BCN": "8", "BCR": "0.114", "RV": "50", "BCD": "0.06", "BCM": "8.1", "HiPP": "5", "LoPP": "3.5", "Vv": "0.8", "MinCP": "8.0", "MaxCP": "10.0", "CompressorSpeed": "0.0613", "CompressorPower": "Converter", "BCS": "1", "BSA": "2", "BM": "P10-Bgu", "BVV": "250", "BRE": "0.9", "UBB1": "8", "HandlePipeUnlockPos": "-0.5", "LPOn": "2.75", "LPOff": "4.5"},
"SpringBrake": {"Volume": "10", "MBF": "8", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "3.5", "ValveOnArea": "0.0002", "ValveOffArea": "0.0002", "ValvePNBArea": "0.002", "MTC": "4"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "39", "ControlType": "ST45"},
"Cntrl": {"BrakeHandle": "MHZ_K8P", "LocBrakeHandle": "FD1", "BrakeSystem": "Pneumatic", "BCPN": "10", "BDelay1": "15", "BDelay2": "6", "BDelay3": "36", "BDelay4": "32", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "MCPN": "4", "SCPN": "3", "AutoRelay": "Yes", "IniCDelay": "0.5", "SCDelay": "0.1", "BrakeDelays": "GPR", "ConverterStart": "Automatic", "ConverterStartDelay": "5", "MotorBlowersStart": "Converter", "FuelStart": "Automatic", "OilStart": "Automatic", "WaterStart": "Battery", "EIMCtrlType": "3", "BrakeOpModes": "PN", "DirChangeMaxPos": "2", "rows": [
["-1","0.7","-1","15","Pneumatic"],
["0","0.5","-1","9","Pneumatic"],
["1","0.46","-1","9","Pneumatic"],
["2","0.42","-1","9","Pneumatic"],
["3","0.38","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.29","-1","9","Pneumatic"],
["6","0","-1","15","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "227.8"},
"Engine": {"AIM": "0.9", "EngineType": "DumbDE", "Trans": "19:64", "Ftmax": "326000", "Flat": "0", "Vhyp": "7.2", "Vadd": "5.5", "Cr": "1.0", "RelayType": "45", "WaterMinTemperature": "40", "WaterFlowTemperature": "70", "WaterCoolingTemperature": "82", "WaterMaxTemperature": "91", "WaterShutters": "Yes", "WaterAuxCircuit": "Yes", "WaterAuxCoolingTemperature": "60", "WaterAuxMaxTemperature": "74", "WaterAuxShutters": "Yes", "OilMinPressure": "0.15", "HeatKFO2": "24", "HeatingRPM": "1096", "MotorBlowersSpeed": "-10", "MotorBlowersSustainTime": "600", "MotorBlowersStartVelocity": "0.1"},
"SpeedControl": {"SpeedCtrl": "Yes", "SpeedButtons": "0|30|40|50|60|70|80|90|100|120", "OverrideManual": "Yes", "InitPwr": "0.3", "MaxPwrVel": "5", "StartVel": "2", "VelStep": "5", "PwrStep": "0.1", "MinPwr": "0.3", "MaxPwr": "1.0", "MinVel": "0", "MaxVel": "120", "Offset": "-0.5", "kPpos": "0.1", "kPneg": "0.4", "kIpos": "0.06", "kIneg": "0.06", "BrakeIntervention": "Yes"},
"MotorParamTable": {"rows": [
["0","20","100","3000","20.8","1340","1713"],
["1","18.5","110","2700","25","1340","1713"],
["2","17.2","135","2500","30","1340","1713"],
["3","14.3","170","2000","48.5","1340","1713"]
]},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "3300", "IminLo": "2500"},
"WWList": {"Size": "4", "rows": [
["600","0","0","0"],
["1365","1131","816","3026"],
["1410","1196","864","3204"],
["1455","1264","912","3382"],
["1500","1320","960","3560"]
]},
"LightsList": {"Size": "8", "Wrap": "no", "Default": "4", "rows": [
["38","34"],
["38","0"],
["34","34"],
["0","0"],
["21","34"],
["22","34"],
["01","16"],
["16","01"]
]},
"UCList": {"Size": "4", "IntegratedBrakePN": "no", "IntegratedBrake": "no", "rows": [
["0","0","0","0","0","0.15","0.15","0","1","0"],
["1","0","0","0","0","0.15","0.15","3","3","1"],
["2","0","0","1","0","0.00","0.15","3","4","1"],
["3","0","0","1","0","0.15","0.00","4","5","3"],
["4","0","0","1","1","0.15","0.15","4","5","3"]
]}
}

View File

@@ -0,0 +1,70 @@
{
"_source": "static/sug/sug_kielce.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "120000", "Mred": "18000", "Vmax": "100", "PWR": "1860", "SandCap": "700", "Type": "et22"},
"Load": {"MaxLoad": "52", "LoadQ": "pieces", "LoadAccepted": "pantstate", "LoadSpeed": "1", "UnLoadSpeed": "0.1"},
"Dimensions": {"L": "16.820", "H": "4.559", "W": "3.005", "Cx": "0.585"},
"Wheels": {"D": "1.250", "AIM": "2000", "Tw": "1.435", "Axle": "Co'Co'", "Ad": "2.1", "Bd": "8.6", "Rmin": "100", "BearingType": "Roll"},
"Brake": {"BrakeValve": "W", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.1", "LocMaxBP": "5.0", "BCN": "8", "BCR": "0.114", "BCD": "0.12", "BCM": "7.0", "HiPP": "5", "LoPP": "3.5", "Vv": "0.88", "MinCP": "7.0", "MaxCP": "8.0", "CompressorSpeed": "0.0567", "BCS": "0.8", "BSA": "0", "BM": "P10-Bg", "BVV": "200", "CompressorPower": "Main", "BRE": "0.75"},
"Doors": {"OpenCtrl": "DriverCtrl", "CloseCtrl": "DriverCtrl", "OpenSpeed": "0.3", "CloseSpeed": "0.1", "DoorMaxShiftL": "0.53", "DoorMaxShiftR": "0.53", "DoorOpenMethod": "Shift", "DoorClosureWarning": "Yes"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "850", "beta": "0.5", "AllowedFlag": "0"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "4", "BDelay1": "15", "BDelay2": "4", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "ManualBrake": "Yes", "DynamicBrake": "Passive", "MCPN": "36", "SCPN": "3", "AutoRelay": "No", "IniCDelay": "0.5", "SCDelay": "0.23", "BrakeDelays": "GP", "BrakeHandle": "Knorr", "LocBrakeHandle": "Knorr", "rows": [
["-1","0.90","0","15","Pneumatic"],
["0","0.50","0","9","Pneumatic"],
["1","-1","-1","0","Pneumatic"],
["2","-1","-1","5","Pneumatic"],
["3","0.001","-1","9","Pneumatic"],
["4","0.00","-1","15","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Power": {"EnginePower": "CurrentCollector", "CollectorsNo": "2", "MaxVoltage": "3600", "MaxCurrent": "1500", "MinH": "0.38", "MaxH": "2.4", "CSW": "1.4", "MinV": "1800", "MinPress": "3.5", "MaxPress": "5.0"},
"Engine": {"EngineType": "ElectricSeriesMotor", "Volt": "1500", "Trans": "24:85", "WindingRes": "0.1782", "nmax": "1500"},
"Switches": {"Pantograph": "impulse"},
"MotorParamTable0": {"rows": [
["0","24.84","85","0.02","167.8","85","0.02"],
["1","21.37","180","-0.15","144.4","180","-0.15"],
["2","20.67","230","-0.15","139.7","230","-0.15"],
["3","21.69","190","-0.04","145","190","-0.04"]
]},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "500", "ImaxHi": "500", "IminLo": "250", "IminHi": "250"},
"RList": {"Size": "36", "RVent": "Automatic", "RVentnmax": "3300", "RVentCutOff": "50", "rows": [
["0","0","0","0","0"],
["1","26.43","1","6","0"],
["2","18.43","1","6","0"],
["3","13.49","1","6","0"],
["4","10.87","1","6","0"],
["5","9.40","1","6","0"],
["6","7.97","1","6","0"],
["7","6.56","1","6","0"],
["8","5.56","1","6","0"],
["9","4.59","1","6","0"],
["10","3.36","1","6","0"],
["11","2.67","1","6","0"],
["12","2.04","1","6","0"],
["13","1.48","1","6","0"],
["14","0.91","1","6","0"],
["15","0.37","1","6","0"],
["16","0.00","1","6","0"],
["17","3.72","2","3","0"],
["18","2.68","2","3","0"],
["19","2.40","2","3","0"],
["20","1.98","2","3","0"],
["21","1.88","2","3","0"],
["22","1.26","2","3","0"],
["23","0.98","2","3","0"],
["24","0.72","2","3","0"],
["25","0.40","2","3","0"],
["26","0.21","2","3","0"],
["27","0.00","2","3","0"],
["28","1.59","3","2","0"],
["29","1.16","3","2","0"],
["30","0.86","3","2","0"],
["31","0.65","3","2","0"],
["32","0.48","3","2","0"],
["33","0.40","3","2","0"],
["34","0.25","3","2","0"],
["35","0.14","3","2","0"],
["36","0.00","3","2","0"]
]}
}

View File

@@ -0,0 +1,47 @@
{
"_source": "pkp/su45_v2/301d.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "102000", "Mred": "15000", "Vmax": "120", "PWR": "1250", "SandCap": "400", "HeatingP": "300"},
"Dimensions": {"L": "18.99", "H": "4.238", "W": "2.900", "Cx": "0.2"},
"Wheels": {"D": "1.100", "AIM": "4000", "Tw": "1.435", "Axle": "Co'Co'", "Ad": "1.950", "Bd": "10.3", "Rmin": "100"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.1", "LocMaxBP": "4.5", "MaxASBP": "1.5", "RM": "0.69", "BCN": "8", "BCR": "0.114", "BCD": "0.06", "BCM": "8.1", "HiPP": "5", "LoPP": "3.5", "Vv": "0.8", "MinCP": "7.0", "MaxCP": "8.5", "CompressorSpeed": "0.053", "BCS": "1", "BSA": "2", "BM": "P10-Bgu", "CompressorPower": "Engine", "BVV": "240", "BRE": "0.9"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "103", "ControlType": "SU45"},
"Cntrl": {"BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "6", "BDelay3": "36", "BDelay4": "32", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "MCPN": "14", "SCPN": "3", "AutoRelay": "Yes", "IniCDelay": "0.5", "SCDelay": "0.1", "BrakeDelays": "GPR", "MotorBlowersStart": "Manual", "ConverterStart": "Automatic", "rows": [
["-1","0.7","-1","15","Pneumatic"],
["0","0.5","-1","9","Pneumatic"],
["1","0.46","-1","9","Pneumatic"],
["2","0.42","-1","9","Pneumatic"],
["3","0.38","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.29","-1","9","Pneumatic"],
["6","0","-1","15","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "227.8"},
"TurboPos": {"TurboPos": "4"},
"Engine": {"AIM": "1.6", "EngineType": "DumbDE", "Trans": "18:62", "Ftmax": "326000", "Flat": "0", "Vhyp": "7.2", "Vadd": "5.5", "Cr": "1.0", "RelayType": "45", "WaterMinTemperature": "40", "WaterFlowTemperature": "70", "WaterCoolingTemperature": "82", "WaterMaxTemperature": "91", "WaterShutters": "Yes", "WaterAuxCircuit": "Yes", "WaterAuxCoolingTemperature": "60", "WaterAuxMaxTemperature": "74", "WaterAuxShutters": "Yes", "OilMinPressure": "0.15", "HeatKFO2": "24", "HeatingRPM": "1096", "MotorBlowersSpeed": "-10"},
"Clima": {"HMaxVoltage": "3000", "Heating": "Generator", "HGeneratorEngine": "Main", "HGeneratorMinRPM": "696", "HGeneratorMinVoltage": "2400", "HGeneratorMaxRPM": "1500", "HGeneratorMaxVoltage": "3300"},
"MotorParamTable": {"rows": [
["0","20","100","3000","20.8","1340","0"],
["1","18.5","110","2700","25","1340","40"],
["2","17.2","135","2500","30","1340","50"],
["3","14.3","170","2000","48.5","1340","1713"]
]},
"WWList": {"Size": "14", "rows": [
["696","0","0","0"],
["696","0","0","0"],
["629","96","78","273"],
["763","192","147","547"],
["830","288","221","820"],
["897","384","295","1094"],
["964","480","369","1367"],
["1031","576","442","1641"],
["1098","672","516","1914"],
["1165","768","590","2187"],
["1232","864","664","2461"],
["1299","960","737","2734"],
["1366","1056","811","3007"],
["1433","1152","885","3281"],
["1500","1245","960","3560"]
]}
}

View File

@@ -0,0 +1,50 @@
{
"_source": "pkp/su46_v2/303d.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "105000", "Mred": "15000", "Vmax": "120", "PWR": "1400", "SandCap": "400", "HeatingP": "440"},
"Dimensions": {"L": "18.99", "H": "4.292", "W": "2.904", "Cx": "0.3"},
"Wheels": {"D": "1.100", "AIM": "4000", "Tw": "1.435", "Axle": "Co'Co'", "Ad": "1.75", "Bd": "10.3", "Rmin": "100"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "85.0", "MaxBP": "4.4", "LocMaxBP": "4.4", "MaxASBP": "1.3", "RM": "0.71", "BCN": "12", "BCR": "0.114", "BCD": "0.06", "BCM": "5.84", "HiPP": "5", "LoPP": "3.5", "Vv": "0.9", "MinCP": "7", "MaxCP": "8.5", "CompressorSpeed": "0.05", "BCS": "1", "BSA": "2", "BM": "P10-Bgu", "CompressorPower": "Converter", "BVV": "200", "BRE": "0.8"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "103", "ControlType": "SU46"},
"Cntrl": {"BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "6", "BDelay3": "36", "BDelay4": "32", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "MCPN": "15", "SCPN": "4", "AutoRelay": "Yes", "IniCDelay": "0.5", "SCDelay": "0.1", "BrakeDelays": "GPR", "ConverterStart": "Automatic", "ConverterStartDelay": "10", "MotorBlowersStart": "Manual", "rows": [
["-1","0.7","-1","15","Pneumatic"],
["0","0.5","-1","9","Pneumatic"],
["1","0.46","-1","9","Pneumatic"],
["2","0.42","-1","9","Pneumatic"],
["3","0.38","-1","9","Pneumatic"],
["4","0.34","-1","9","Pneumatic"],
["5","0.29","-1","9","Pneumatic"],
["6","0","-1","15","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "100"},
"Engine": {"AIM": "1.6", "EngineType": "DumbDE", "Trans": "18:62", "Ftmax": "317000", "Flat": "0", "Vhyp": "15.0", "Vadd": "3.8", "Cr": "1.0", "RelayType": "46", "HeatingRPM": "1098", "OilMinPressure": "0.055", "OilMaxPressure": "0.6", "WaterMinTemperature": "40", "WaterMaxTemperature": "92", "WaterCoolingTemperature": "76", "WaterFlowTemperature": "71", "WaterShutters": "Yes", "WaterAuxCircuit": "Yes", "WaterAuxFlowTemperature": "1", "WaterAuxMinTemperature": "35", "WaterAuxMaxTemperature": "71", "WaterAuxCoolingTemperature": "56", "WaterAuxShutters": "Yes", "HeaterMinTemperature": "50", "HeaterMaxTemperature": "85", "HeatKW": "0.068599", "HeatKV": "0.3", "HeatKFE": "1", "HeatKFS": "87.76269", "HeatKFO": "26.72716", "HeatKFO2": "57.72", "WaterCoolingFanSpeed": "0.56", "MotorBlowersSpeed": "-10"},
"TurboPos": {"TurboPos": "4"},
"Circuit": {"CircuitRes": "0.01", "ImaxLo": "3300", "IminLo": "2500"},
"Clima": {"HMaxVoltage": "3000", "Heating": "Generator", "HGeneratorEngine": "Main", "HGeneratorMinRPM": "696", "HGeneratorMinVoltage": "2400", "HGeneratorMaxRPM": "1500", "HGeneratorMaxVoltage": "3300"},
"MotorParamTable": {"rows": [
["0","16.7","100","2000","24.1","1680","0"],
["1","16.9","155","2250","36.9","40","40"],
["2","17.1","210","2500","50.8","1680","1680"],
["3","17.3","265","2800","65.0","50","50"],
["4","17.5","321","3125","79.9","0","1680"]
]},
"WWList": {"Size": "15", "rows": [
["696","0","0","0"],
["696","0","0","0"],
["696","80","350","1000"],
["696","80","350","1000"],
["763","214","455","1400"],
["830","321","500","1600"],
["897","428","545","1800"],
["964","535","590","2000"],
["1031","642","635","2200"],
["1098","748","680","2400"],
["1165","855","725","2600"],
["1232","962","770","2800"],
["1299","1069","815","3000"],
["1366","1176","865","3200"],
["1433","1283","910","3400"],
["1500","1390","960","3600"]
]}
}

View File

@@ -0,0 +1,39 @@
{
"_source": "pkp/tem2_v2/tem2a.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "116000", "Mred": "20000", "Vmax": "100", "PWR": "780", "SandCap": "2000"},
"Dimensions": {"L": "17", "H": "4.62", "W": "2.95", "Cx": "0.1"},
"Wheels": {"D": "1.05", "AIM": "4000", "Tw": "1.435", "Axle": "Co'Co'", "Ad": "2.1", "Bd": "8.6", "Rmin": "80", "BearingType": "Roll"},
"Brake": {"BrakeValve": "LSt", "NBpA": "2", "MBF": "124", "MaxBP": "4.1", "MaxLBP": "3.9", "BCN": "4", "BCR": "0.127", "BCD": "0.1", "BCM": "10.77", "HiPP": "5.0", "LoPP": "3.5", "Vv": "0.888", "MinCP": "7", "MaxCP": "8.5", "CompressorSpeed": "0.077", "CompressorPower": "Main", "BCS": "1", "BSA": "0", "BM": "P10-Bgu", "BVV": "222", "BRE": "0.85"},
"BuffCoupl1": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "3"},
"BuffCoupl2": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "23", "ControlType": "TEM2"},
"Cntrl": {"BrakeHandle": "M394", "LocBrakeHandle": "FD1", "BrakeDelays": "G", "BrakeSystem": "Pneumatic", "BCPN": "5", "BDelay1": "25", "BDelay2": "13", "BDelay3": "25", "BDelay4": "13", "ASB": "None", "LocalBrake": "PneumaticBrake", "MCPN": "8", "SCPN": "2", "AutoRelay": "Yes", "IniCDelay": "0.5", "SCDelay": "0.1", "rows": [
["-1","1.0","-1","10","Pneumatic"],
["0","0.5","-1","4","Pneumatic"],
["1","-1","-1","0","Pneumatic"],
["2","-1","-1","2","Pneumatic"],
["3","0.0","-1","2","Pneumatic"],
["4","0.0","-1","6","Pneumatic"],
["5","0.0","-1","15","Pneumatic"]
]},
"Security": {"AwareSystem": "Active,CabSignal", "AwareDelay": "60", "SoundSignalDelay": "5", "MaxHoldTime": "1.0", "EmergencyBrakeDelay": "6", "RadioStop": "Yes"},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "75", "LMaxCurrent": "75"},
"TurboPos": {"TurboPos": "3"},
"Engine": {"AIM": "0.9", "EngineType": "DieselElectric", "Trans": "15:68", "Ftmax": "396000", "Flat": "1", "Vhyp": "15", "Vadd": "0", "Cr": "1.107", "OilMinPressure": "0.16", "OilMaxPressure": "0.3", "WaterCoolingFanSpeed": "1.41", "WaterMaxTemperature": "88", "WaterCoolingTemperature": "76", "WaterShutters": "yes"},
"MotorParamTable": {"rows": [
["0","24","66","2000","14","1000","0"],
["1","18","70","1600","17","1000","1400"],
["2","15","106","1400","26","0","1400"]
]},
"WWList": {"Size": "8", "rows": [
["300","0","0","0"],
["290","65","870","1900"],
["270","166","870","1900"],
["330","268","870","1900"],
["400","370","870","1900"],
["480","471","870","1900"],
["570","573","870","1900"],
["650","647","870","1900"],
["750","780","870","1900"]
]}
}

View File

@@ -0,0 +1,22 @@
{
"_source": "pkp/wm15_v1/wm15.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "20000", "Vmax": "80", "PWR": "200", "SandCap": "400"},
"Dimensions": {"L": "11", "H": "4.292", "W": "2.900", "Cx": "0.3"},
"Wheels": {"D": "1.100", "AIM": "4000", "Tw": "1.435", "Axle": "Bo'Bo'", "Ad": "2.6", "Bd": "10.1", "Rmin": "80"},
"Brake": {"BrakeValve": "LSt", "NBpA": "4", "MBF": "85.0", "MaxBP": "3.8", "BCN": "4", "BCR": "0.0625", "BCD": "0.11", "BCM": "5.14", "HiPP": "5.0", "LoPP": "3.5", "Vv": "0.250", "MinCP": "7.5", "MaxCP": "8.5", "CompressorSpeed": "0.03", "BVV": "52", "BRE": "0.8", "BM": "P10-Bg", "BCS": "0.8", "BSA": "0"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "3"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "MCPN": "8", "SCPN": "3", "AutoRelay": "No", "IniCDelay": "0.5", "SCDelay": "0.1", "BrakeDelays": "GP", "BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "rows": [
["-1","0.8","-1","20","Pneumatic"],
["0","0.5","-1","2","Pneumatic"],
["1","0.46","-1","2","Pneumatic"],
["2","0.42","-1","2","Pneumatic"],
["3","0.38","-1","2","Pneumatic"],
["4","0.34","-1","2","Pneumatic"],
["5","0.29","-1","2","Pneumatic"],
["6","0","-1","40","Pneumatic"]
]},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "110", "LMaxCurrent": "155"},
"Power": {"EnginePower": "Internal"},
"Engine": {"EngineType": "Dumb", "Ftmax": "250000"}
}

View File

@@ -0,0 +1,49 @@
{
"_source": "pkp/wmb10_v1/wmb10.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "9800", "Vmax": "70", "PWR": "92", "SandCap": "0", "Mred": "1000"},
"Dimensions": {"L": "8.21", "H": "2.9", "W": "2.8", "Cx": "0.2"},
"Load": {"MaxLoad": "6", "LoadQ": "pieces", "LoadAccepted": "Passengers,naprawcze,brygada_naprawcza,konserwacja", "OverLoadFactor": "1", "LoadSpeed": "0.5", "UnLoadSpeed": "0.5"},
"Wheels": {"D": "0.7", "AIM": "2000", "Tw": "1.435", "Axle": "1'A", "Bd": "5.1", "Rmin": "80", "BearingType": "Roll"},
"Doors": {"OpenCtrl": "Passenger", "CloseCtrl": "Passenger", "OpenSpeed": "0.2", "CloseSpeed": "0.2", "DoorOpenMethod": "Rotate", "DoorMaxShiftL": "0.625", "DoorMaxShiftR": "0.625"},
"Brake": {"BrakeValve": "LSt", "Size": "12", "NBpA": "1", "MBF": "11", "MaxBP": "4.0", "BCN": "2", "BCR": "0.1016", "BCD": "0.15", "BCM": "4.1", "BCS": "1.0", "BSA": "1.0", "BM": "Disk2", "BVV": "50", "BRE": "0.9", "MinCP": "6", "MaxCP": "7.3", "Vv": "0.2", "CompressorSpeed": "0.0125", "CompressorPower": "Main"},
"SpringBrake": {"Volume": "9.4", "MBF": "11", "MaxSP": "5.5", "ResetP": "4.0", "MinFP": "3.5", "PressOff": "4.5", "PressOn": "4.2", "ValveOnArea": "0.0005", "ValveOffArea": "0.0005", "ValvePNBArea": "0.002", "MTC": "132"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.15", "FmaxB": "740", "kC": "4800", "DmaxC": "0.15", "FmaxC": "780", "beta": "0.58", "AllowedFlag": "3"},
"Cntrl": {"BrakeSystem": "Pneumatic", "BCPN": "6", "BDelay1": "15", "BDelay2": "4", "BDelay3": "35", "BDelay4": "25", "ASB": "Manual", "LocalBrake": "PneumaticBrake", "MCPN": "9", "SCPN": "5", "AutoRelay": "no", "IniCDelay": "0.48", "SCDelay": "0.3", "BrakeDelays": "P", "BrakeHandle": "FV4a", "LocBrakeHandle": "FD1", "FuelStart": "Automatic", "OilStart": "Automatic", "WaterStart": "Battery", "ConverterStart": "Automatic", "rows": [
["-1","0.8","-1","20","Pneumatic"],
["0","0.5","-1","2","Pneumatic"],
["1","0.46","-1","2","Pneumatic"],
["2","0.42","-1","2","Pneumatic"],
["3","0.38","-1","2","Pneumatic"],
["4","0.34","-1","2","Pneumatic"],
["5","0.29","-1","2","Pneumatic"],
["6","0","-1","40","Pneumatic"]
]},
"Light": {"Light": "Generator", "LGeneratorEngine": "ElectricSeriesMotor", "LMaxVoltage": "24", "LMaxCurrent": "100"},
"Engine": {"EngineType": "DieselEngine", "Trans": "10:54", "nmin": "575", "nmax": "2700", "nmax_cutoff": "2600", "AIM": "10", "EUS": "20", "EDS": "20"},
"MotorParamTable": {"minVelfullengage": "1.62", "engageDia": "1", "engageMaxForce": "384", "engagefriction": "1", "rows": [
["0","0","0","-1"],
["1","6.93","-1","10"],
["2","4.03","4","17"],
["3","2.36","9","30"],
["4","1.40","18","53"],
["5","1.00","33","100"]
]},
"DList": {"Size": "9", "Mmax": "450", "nMmax": "26.66", "Mnmax": "370", "nmax": "43.33", "nominalfill": "1", "Mstand": "130", "rows": [
["0","0.4","0"],
["1","0.46","1"],
["2","0.52","2"],
["3","0.58","2"],
["4","0.64","2"],
["5","0.70","2"],
["6","0.76","2"],
["7","0.82","2"],
["8","0.88","2"],
["9","1.0","2"]
]},
"LightsList": {"Size": "3", "Wrap": "No", "Default": "2", "rows": [
["34","21"],
["0","0"],
["21","34"]
]}
}

View File

@@ -0,0 +1,14 @@
{
"_source": "pkp/z1_v2/152a.fiz",
"_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads",
"Param": {"Category": "train", "M": "48000", "Vmax": "200", "HeatingP": "54", "LightP": "0.56"},
"Load": {"MaxLoad": "80", "LoadQ": "pieces", "LoadAccepted": "Passengers,Luggage", "OverLoadFactor": "1.5", "LoadSpeed": "1", "UnLoadSpeed": "2"},
"Dimensions": {"L": "26.4", "H": "4", "W": "2.9", "Cx": "0.01"},
"Wheels": {"D": "0.92", "Tw": "1.435", "Axle": "2'2'", "Ad": "2.70", "Bd": "19", "Rmin": "100", "BearingType": "Roll"},
"Brake": {"BrakeValve": "KE", "MBF": "11", "MaxBP": "3.8", "BCN": "12", "BCR": "0.127", "BCD": "0.05", "BCM": "1.42", "NBpA": "6", "RM": "1.4", "BM": "Disk1+Mg", "BCS": "1.2", "BSA": "0", "BVV": "130", "TBF": "32"},
"BuffCoupl": {"CType": "Screw", "kB": "9200", "DmaxB": "0.11", "FmaxB": "320", "kC": "4500", "DmaxC": "0.1", "FmaxC": "850", "beta": "0.58", "AllowedFlag": "115"},
"Cntrl": {"BrakeSystem": "Pneumatic", "LocalBrake": "ManualBrake", "BrakeDelays": "PR+Mg", "ASB": "Yes", "rows": []},
"Light": {"Light": "Generator", "LGeneratorEngine": "WheelsDriven", "LMaxVoltage": "24", "AlterLight": "Accu", "AlterLMaxVoltage": "24", "AlterLCap": "495", "AlterLRS": "Generator"},
"Clima": {"Heating": "PowerCable", "HPowerTrans": "ElectricPower", "HMaxVoltage": "3000"},
"Doors": {"OpenCtrl": "Passenger", "CloseCtrl": "AutomaticCtrl", "OpenSpeed": "0.5", "CloseSpeed": "0.20", "DoorMaxShiftL": "0.9", "DoorMaxShiftR": "0.9", "DoorOpenMethod": "Plug", "DoorClosureWarning": "yes", "DoorBlocked": "Yes", "DoorMaxShiftPlug": "0.043"}
}

4
tests/unit/data/golden/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# the golden traces are recorded on the code before a change, not kept: the samples and their key figures
# (see tests/unit/README.md)
*.csv
*.figures

246
tests/unit/drive.cpp Normal file
View File

@@ -0,0 +1,246 @@
#include "stdafx.h"
#include "drive.h"
#include "consist.h"
#include "golden.h"
#include "doctest.h"
#include "McZapkie/hamulce.h"
namespace test {
void mark(trace *Trace, std::string const &Event) {
if (Trace != nullptr) {
Trace->mark(Event);
}
}
bool is_powered(TMoverParameters const &Vehicle) {
using enum TEngineType;
auto const type{Vehicle.EngineType};
return Vehicle.Power > 1.0 && (type == ElectricSeriesMotor || type == ElectricInductionMotor || type == DieselEngine || type == DieselElectric);
}
driven driving(vehicle &Vehicle) {
return {Vehicle.mover(), Vehicle.mover(), Vehicle.mover(), [&Vehicle](double Seconds, track const &Track) { Vehicle.run(Seconds, Track); }};
}
driven driving(consist &Consist) {
// the AI's mvControlling and mvPantographUnit: the first vehicle with an engine, and with pantographs
auto *controlling{&Consist[0].mover()};
auto *pantographs{&Consist[0].mover()};
for (auto i = Consist.size(); i-- > 0;) {
auto &mover{Consist[i].mover()};
if (is_powered(mover)) {
controlling = &mover;
}
if (mover.EnginePowerSource.SourceType == TPowerSource::CurrentCollector && mover.EnginePowerSource.CollectorParameters.CollectorsNo > 0) {
pantographs = &mover;
}
}
return {Consist[0].mover(), *controlling, *pantographs, [&Consist](double Seconds, track const &Track) { Consist.run(Seconds, Track); }};
}
namespace {
void attend(TMoverParameters &Mover) {
if (Mover.SecuritySystem.is_cabsignal_blinking() && Mover.SecuritySystem.has_separate_acknowledge()) {
Mover.SecuritySystem.cabsignal_reset();
}
if (Mover.SecuritySystem.is_blinking()) {
Mover.SecuritySystemReset();
}
}
} // namespace
void drive(driven Driven, track const &Track, double Seconds) {
auto const seconds{std::lround(Seconds)};
for (long second = 0; second < seconds; ++second) {
attend(Driven.cab);
Driven.run(1.0, Track);
}
}
void release_brakes(TMoverParameters &Cab) {
Cab.BrakeLevelSet(Cab.Handle->GetPos(bh_RP));
Cab.DecLocalBrakeLevel(LocalBrakePosNo);
Cab.DecManualBrakeLevel(ManualBrakePosNo);
Cab.SpringBrakeActivate(false);
// the hand and spring brakes of the rest of the train, as TController::AutoRewident() releases them
std::vector train{&Cab};
std::vector pending{&Cab};
while (false == pending.empty()) {
auto const *vehicle{pending.back()};
pending.pop_back();
for (auto const &thiscoupler : vehicle->Couplers) {
if (thiscoupler.Connected != nullptr && thiscoupler.CouplingFlag != coupling::faux && std::ranges::find(train, thiscoupler.Connected) == train.end()) {
thiscoupler.Connected->DecManualBrakeLevel(ManualBrakePosNo);
thiscoupler.Connected->SpringBrake.Activate = false;
train.push_back(thiscoupler.Connected);
pending.push_back(thiscoupler.Connected);
}
}
}
}
bool is_ready(driven const &Driven) {
auto const &mover{Driven.controlling};
auto const air{mover.VeselVolume <= 0.0 || mover.ScndPipePress >= 4.5};
// the converter feeds the 110 V circuits (the electrodynamic brake's control among them); the AI switches
// it on once the line breaker is closed, so a train already charged with air isn't ready before it runs
auto const converter{mover.ConverterStart != start_t::manual || mover.ConverterFlag};
return mover.Mains && air && converter;
}
namespace {
// the pumps and the heater of a diesel's water, and the fuel pump
void prepare_diesel(driven const &Driven) {
Driven.cab.OilPumpSwitch(true);
Driven.controlling.WaterPumpBreakerSwitch(true);
Driven.controlling.WaterPumpSwitch(true);
Driven.controlling.WaterHeaterBreakerSwitch(true);
Driven.controlling.WaterHeaterSwitch(true);
Driven.cab.FuelPumpSwitch(true);
}
// the pantographs' own air topped up by their compressor, and the pantographs raised
void raise_pantographs(driven const &Driven) {
auto &pantographs{Driven.pantographs};
if (auto const enough{(pantographs.TrainType == dt_EZT ? 2.5 : 3.5) + 0.1}; pantographs.PantPress < enough) {
if (false == pantographs.PantAutoValve) {
pantographs.bPantKurek3 = false; // the pantographs on their own air
}
pantographs.PantCompFlag = true;
}
else if (false == pantographs.bPantKurek3 || pantographs.PantPress <= pantographs.ScndPipePress) {
pantographs.PantCompFlag = false;
}
Driven.cab.OperatePantographsValve(operation_t::enable);
Driven.cab.OperatePantographValve(end::rear, operation_t::enable);
}
// the line breaker closed, which it does only with the controller at zero, and after the relays are reset
void close_line_breaker(driven const &Driven) {
auto &controlling{Driven.controlling};
while (controlling.DecMainCtrl(2)) {
// down to zero
}
controlling.DecScndCtrl(2);
Driven.cab.RelayReset(relay_t::maincircuitground | relay_t::tractionnmotoroverload | relay_t::primaryconverteroverload);
if (controlling.EngineType == TEngineType::DieselEngine) {
// a mechanical diesel starts at idle, the first position giving the engine fuel
while (controlling.RList[controlling.MainCtrlPos].Mn == 0 && controlling.IncMainCtrl(1)) {
// up to the first position with fuel
}
}
Driven.cab.MainSwitch(true);
}
// the converter, the compressor and the motor blowers, once the line breaker is closed
void switch_auxiliaries_on(TMoverParameters &Cab) {
Cab.ConverterSwitch(true);
Cab.CompressorSwitch(true);
for (auto const side : {end::front, end::rear}) {
if (Cab.MotorBlowers[side].speed != 0) {
Cab.MotorBlowersSwitchOff(false, side);
Cab.MotorBlowersSwitch(true, side);
}
}
}
} // namespace
bool start_up(driven Driven, track const &Track, double Seconds, trace *Trace) {
auto &cab{Driven.cab};
auto const &controlling{Driven.controlling};
auto const &pantographs{Driven.pantographs};
mark(Trace, "start up");
auto const diesel{controlling.EngineType == TEngineType::DieselEngine || controlling.EngineType == TEngineType::DieselElectric};
auto const collector{pantographs.EnginePowerSource.SourceType == TPowerSource::CurrentCollector &&
pantographs.EnginePowerSource.CollectorParameters.CollectorsNo > 0};
// the converter and the compressor get switched on once the line breaker is closed, wherever in the train
// they are; a train already charged with air isn't ready before that
auto auxiliaries{false};
// one round of the driver's actions a second, as the AI reacts
for (auto second = 0; second < Seconds; ++second) {
cab.BatterySwitch(true);
cab.CabActivisation();
if (diesel) {
prepare_diesel(Driven);
}
if (collector) {
raise_pantographs(Driven);
}
if (false == collector || std::max(controlling.GetTrainsetHighVoltage(), controlling.PantographVoltage) > controlling.EnginePowerSource.CollectorParameters.MinV) {
// a direction first, as in TController::PrepareDirection(): the relays reset only with one set
for (int i = 0; i < 3 && cab.DirActive <= 0; ++i) {
cab.DirectionForward();
}
if (false == controlling.Mains) {
close_line_breaker(Driven);
}
else {
switch_auxiliaries_on(cab);
auxiliaries = true;
}
}
drive(Driven, Track, 1.0);
if (auxiliaries && is_ready(Driven)) {
mark(Trace, "ready");
return true;
}
}
return false;
}
void drive_cycle(driven Driven, track const &Track, trace &Trace) {
auto &cab{Driven.cab};
auto &controlling{Driven.controlling};
if (false == start_up(Driven, Track, 300.0, &Trace)) {
MESSAGE("didn't get ready: Mains " << controlling.Mains << ", main reservoir " << controlling.ScndPipePress);
}
Trace.mark("release brakes");
release_brakes(cab);
drive(Driven, Track, 30.0);
Trace.mark("forward");
for (int i = 0; i < 3 && cab.DirActive <= 0; ++i) {
cab.DirectionForward();
}
drive(Driven, Track, 2.0);
Trace.mark("notch up");
for (int i = 0; i < 60 && false == controlling.IsMainCtrlMaxPowerPos(); ++i) {
controlling.IncMainCtrl(1);
drive(Driven, Track, 2.0);
}
Trace.mark("full power");
drive(Driven, Track, 30.0);
Trace.mark("second controller up");
for (int i = 0; i < 20 && controlling.IncScndCtrl(1); ++i) {
drive(Driven, Track, 2.0);
}
drive(Driven, Track, 20.0);
Trace.mark("second controller down");
for (int i = 0; i < 20 && controlling.DecScndCtrl(1); ++i) {
drive(Driven, Track, 1.0);
}
Trace.mark("notch down");
for (int i = 0; i < 60 && controlling.DecMainCtrl(1); ++i) {
drive(Driven, Track, 1.0);
}
Trace.mark("coast");
drive(Driven, Track, 10.0);
Trace.mark("brake");
cab.BrakeLevelSet(cab.Handle->GetPos(bh_FB));
drive(Driven, Track, 60.0);
Trace.mark("release");
cab.BrakeLevelSet(cab.Handle->GetPos(bh_RP));
drive(Driven, Track, 20.0);
}
} // namespace test

56
tests/unit/drive.h Normal file
View File

@@ -0,0 +1,56 @@
#pragma once
#include <functional>
#include <string>
#include "vehicle.h"
namespace test {
class trace;
class consist;
// what a driver does with a vehicle, made of the steps the AI takes (TController and the driver hints of
// application/driverhints.cpp), on the vehicle's own controls. the driver sits in a vehicle with a cab
// (Cab != 0): a lone one, or the first one of a consist, and drives the whole of it
// what the driver drives: the vehicle with the driver in it, the vehicles the driver controls the engine and
// the pantographs of (the AI's mvOccupied, mvControlling, mvPantographUnit; all the same one in a lone
// vehicle or a locomotive), and the way to run the whole of it
struct driven {
TMoverParameters &cab;
TMoverParameters &controlling;
TMoverParameters &pantographs;
std::function<void(double Seconds, track const &Track)> run;
};
driven driving(vehicle &Vehicle);
driven driving(consist &Consist);
// gets the vehicle ready to run, the way TController::PrepareEngine() does: battery, cab, pumps and heating
// of a diesel engine, pantographs, line breaker, converter, compressor and motor blowers, then waits for the
// air. stops trying after Seconds; returns whether the vehicle got ready
bool start_up(driven Driven, track const &Track, double Seconds = 300.0, trace *Trace = nullptr);
// runs the vehicle for Seconds with the driver paying attention, as TController::control_security_system()
// does: once a second the vigilance and the cab signalling get acknowledged when they call for it
void drive(driven Driven, track const &Track, double Seconds);
// releases every brake the driver has a hand on: the train brake to running, the independent brake, the
// hand brake and the spring brake; and the hand and spring brakes of the vehicles coupled to the cab's
void release_brakes(TMoverParameters &Cab);
// whether the vehicle is ready to run: line breaker closed (or the engine running), air in the main reservoir
bool is_ready(driven const &Driven);
// whether the vehicle drives itself
bool is_powered(TMoverParameters const &Vehicle);
// a trace mark, when there's a trace
void mark(trace *Trace, std::string const &Event);
// a whole run: start up, release the brakes, notch up to full power, the second controller (field weakening,
// shunting) up and down, notch down, coast and brake to a stop. the controllers move on a fixed schedule,
// not the way a driver watching the ammeter would, so the relays may trip on the way
void drive_cycle(driven Driven, track const &Track, trace &Trace);
} // namespace test

222
tests/unit/figures.cpp Normal file
View File

@@ -0,0 +1,222 @@
#include "stdafx.h"
#include "figures.h"
#include <array>
#include <cmath>
#include <cstdio>
#include <cstdlib>
namespace test {
namespace {
// the units of the figures, with the decimals they're written with
struct unit {
char const *name;
int decimals;
};
unit const seconds{"s", 2};
unit const kmh{"km/h", 1};
unit const metres{"m", 1};
unit const bar{"bar", 3};
unit const amperes{"A", 0};
unit const kilonewtons{"kN", 1};
std::array const units{seconds, kmh, metres, bar, amperes, kilonewtons};
std::string line(char const *Name, double Value, unit const &Unit) {
return std::format("{} = {:.{}f} {}", Name, Value, Unit.decimals, Unit.name);
}
// the name, the value and the unit of a line of a figure, or false for the header of a part
bool parse(std::string_view const Line, std::string &Name, double &Value, std::string &Unit) {
auto const equals{Line.find(" = ")};
if (equals == std::string_view::npos) {
return false;
}
Name = Line.substr(0, equals);
std::string const value{Line.substr(equals + 3)};
auto const space{value.find(' ')};
Value = std::strtod(value.substr(0, space).c_str(), nullptr);
Unit = space == std::string::npos ? std::string{} : value.substr(space + 1);
return true;
}
// a difference of 0.5%, or of the last decimal written
bool close(double Recorded, double Now, std::string_view const Unit) {
auto step{0.0};
for (auto const &unit : units) {
if (Unit == unit.name) {
step = std::pow(10.0, -unit.decimals);
}
}
return std::abs(Recorded - Now) <= 0.005 * std::max(std::abs(Recorded), std::abs(Now)) + step + 1e-9;
}
// the drop of the brake pipe which the time to get back counts from, and the pressure it counts to
constexpr double pipe_drop{4.8};
constexpr double pipe_back{4.9};
} // namespace
void figures::sample(double Time, TMoverParameters const &Vehicle) {
auto &part{m_parts.back()};
auto const speed{Vehicle.V * 3.6};
auto const pipe{Vehicle.PipePress};
if (false == part.sampled) {
part.sampled = true;
// from the last step of the part before, so the parts add up to the run
part.start = m_last_time < 0.0 ? Time : m_last_time;
part.speed_start = m_last_time < 0.0 ? speed : m_last_speed;
part.distance_start = m_last_time < 0.0 ? Vehicle.DistCounter : m_last_distance;
part.pipe_lowest = pipe;
}
part.end = Time;
part.speed_end = speed;
part.speed_peak = std::max(part.speed_peak, std::abs(speed));
part.distance_end = Vehicle.DistCounter;
if (part.stopped < 0.0 && std::abs(part.speed_start) > 1.0 && std::abs(speed) < 0.05) {
part.stopped = Time - part.start;
}
if (pipe < part.pipe_lowest) {
part.pipe_lowest = pipe;
part.pipe_back = -1.0;
}
part.pipe_end = pipe;
if (part.pipe_back < 0.0 && part.pipe_lowest < pipe_drop && pipe >= pipe_back) {
part.pipe_back = Time - part.start;
}
part.cylinders_peak = std::max(part.cylinders_peak, Vehicle.BrakePress);
part.current_peak = std::max(part.current_peak, std::abs(Vehicle.Im));
part.force_peak = std::max(part.force_peak, std::abs(Vehicle.Ft) / 1000.0);
m_last_time = Time;
m_last_speed = speed;
m_last_distance = Vehicle.DistCounter;
// which figures the vehicle has, over the whole run, so each part has the same ones
m_moved |= std::abs(speed) >= 0.05;
m_piped |= pipe >= 0.1;
m_braked |= Vehicle.BrakePress >= 0.005;
m_current |= std::abs(Vehicle.Im) >= 0.5;
m_force |= std::abs(Vehicle.Ft) >= 50.0;
}
void figures::mark(std::string const &Event) {
if (auto &part{m_parts.back()}; false == part.sampled) {
// nothing ran since the last event: a part of both
part.event = part.event == "start" ? Event : part.event + ", " + Event;
return;
}
m_parts.emplace_back(Event);
}
std::vector<std::string> figures::lines() const {
std::vector<std::string> result;
for (auto const &part : m_parts) {
if (false == part.sampled) {
continue;
}
result.push_back("[" + part.event + "]");
result.push_back(line("duration", part.end - part.start, seconds));
if (m_moved) {
result.push_back(line("speed at the start", part.speed_start, kmh));
result.push_back(line("speed at the end", part.speed_end, kmh));
result.push_back(line("speed peak", part.speed_peak, kmh));
result.push_back(line("distance", (part.distance_end - part.distance_start) * 1000.0, metres));
if (part.stopped >= 0.0) {
result.push_back(line("stopped after", part.stopped, seconds));
}
}
if (m_piped) {
result.push_back(line("brake pipe lowest", part.pipe_lowest, bar));
result.push_back(line("brake pipe at the end", part.pipe_end, bar));
if (part.pipe_back >= 0.0) {
result.push_back(line("brake pipe back at 4.9 bar after", part.pipe_back, seconds));
}
}
if (m_braked) {
result.push_back(line("brake cylinders peak", part.cylinders_peak, bar));
}
if (m_current) {
result.push_back(line("motor current peak", part.current_peak, amperes));
}
if (m_force) {
result.push_back(line("tractive force peak", part.force_peak, kilonewtons));
}
}
return result;
}
namespace {
struct figure {
std::string name;
double value;
std::string unit;
std::string text; // the value and the unit as written
};
struct recorded_part {
std::string header;
std::vector<figure> figures;
};
std::vector<recorded_part> parts_of(std::vector<std::string> const &Lines) {
std::vector<recorded_part> parts;
for (auto const &line : Lines) {
figure figure;
if (false == parse(line, figure.name, figure.value, figure.unit)) {
parts.emplace_back(line);
continue;
}
if (parts.empty()) {
parts.emplace_back();
}
figure.text = line.substr(line.find(" = ") + 3);
parts.back().figures.push_back(figure);
}
return parts;
}
figure const *find(std::vector<figure> const &Figures, std::string_view const Name) {
for (auto const &figure : Figures) {
if (figure.name == Name) {
return &figure;
}
}
return nullptr;
}
} // namespace
std::vector<std::string> figures::compare(std::vector<std::string> const &Recorded, std::vector<std::string> const &Now) {
std::vector<std::string> differences;
auto const recorded{parts_of(Recorded)};
auto const now{parts_of(Now)};
for (std::size_t i = 0; i < std::max(recorded.size(), now.size()); ++i) {
if (i >= recorded.size() || i >= now.size() || recorded[i].header != now[i].header) {
// the parts can't be paired up from here on
differences.push_back(std::format("the parts differ from part {}: {} now, {} recorded", i + 1, i < now.size() ? now[i].header : std::string{"none"},
i < recorded.size() ? recorded[i].header : std::string{"none"}));
break;
}
auto const &header{now[i].header};
for (auto const &figure : recorded[i].figures) {
auto const *current{find(now[i].figures, figure.name)};
if (current == nullptr) {
differences.push_back(header + " " + figure.name + ": none now, " + figure.text + " recorded");
}
else if (current->unit != figure.unit || false == close(figure.value, current->value, figure.unit)) {
differences.push_back(header + " " + figure.name + ": " + current->text + " now, " + figure.text + " recorded");
}
}
for (auto const &figure : now[i].figures) {
if (find(recorded[i].figures, figure.name) == nullptr) {
differences.push_back(header + " " + figure.name + ": " + figure.text + " now, none recorded");
}
}
}
return differences;
}
} // namespace test

70
tests/unit/figures.h Normal file
View File

@@ -0,0 +1,70 @@
#pragma once
#include <string>
#include <vector>
#include "McZapkie/MOVER.h"
namespace test {
// the key figures of a vehicle's run, for each part of it between two events of the test: the speeds, the
// distance, the time to stop, the brake pipe's lowest pressure and the time it took to get back, the brake
// cylinders', the motor current's and the tractive force's peaks. a golden trace keeps them next to its
// samples, in data/golden/<Name>.figures, which a person reads:
//
// [emergency]
// duration = 40.00 s
// speed at the start = 80.0 km/h
// speed at the end = 0.0 km/h
// distance = 412.3 m
// stopped after = 38.21 s
// brake cylinders peak = 3.800 bar
//
// a figure the vehicle doesn't have (no speed, no current) is left out of all the parts. they're compared
// within 0.5%, loose enough for any compiler, so a change which alters the behaviour on purpose shows in their
// diff in a few readable lines, where the samples change in thousands
class figures {
public:
// called on every step of the vehicle
void sample(double Time, TMoverParameters const &Vehicle);
// a part of the run starts with the event
void mark(std::string const &Event);
// the figures as the lines of the file
std::vector<std::string> lines() const;
// the differences of the lines from the recorded ones, beyond the tolerance, each told in a line
static std::vector<std::string> compare(std::vector<std::string> const &Recorded, std::vector<std::string> const &Now);
private:
struct part {
std::string event;
bool sampled{false};
double start{0.0};
double end{0.0};
double speed_start{0.0};
double speed_end{0.0};
double speed_peak{0.0};
double distance_start{0.0};
double distance_end{0.0};
double stopped{-1.0}; // after the start, if it started moving and stopped
double pipe_lowest{0.0};
double pipe_end{0.0};
double pipe_back{-1.0}; // after the start, at 4.9 bar again after a drop under 4.8
double cylinders_peak{0.0};
double current_peak{0.0};
double force_peak{0.0};
};
std::vector<part> m_parts{part{"start"}};
double m_last_time{-1.0};
double m_last_speed{0.0};
double m_last_distance{0.0};
// the figures the vehicle has, shown in every part
bool m_moved{false};
bool m_piped{false};
bool m_braked{false};
bool m_current{false};
bool m_force{false};
};
} // namespace test

222
tests/unit/fiz.cpp Normal file
View File

@@ -0,0 +1,222 @@
#include "stdafx.h"
#include "fiz.h"
#include <algorithm>
#include <array>
#include <format>
#include "utilities/parser.h"
namespace test::fiz {
namespace {
// the lists of LoadFIZ_Parse(), as bits of the set of the ones reading a row
constexpr unsigned brake_table{1 << 0}; // startBPT
constexpr unsigned motor_table{1 << 1}; // startMPT
constexpr unsigned motor_table0{1 << 2}; // startMPT0
constexpr unsigned resistors{1 << 3}; // startRLIST
constexpr unsigned universal{1 << 4}; // startUCLIST
constexpr unsigned diesel{1 << 5}; // startDLIST
constexpr unsigned diesel_momentum{1 << 6}; // startDIZELMOMENTUMLIST
constexpr unsigned diesel_v2n{1 << 7}; // startDIZELV2NMAXLIST
constexpr unsigned hydro{1 << 8}; // startHYDROTCLIST
constexpr unsigned diesel_pmax{1 << 9}; // startPMAXLIST
constexpr unsigned field{1 << 10}; // startFFLIST
constexpr unsigned field_brake{1 << 11}; // startFFEDLIST
constexpr unsigned generator{1 << 12}; // startWWLIST
constexpr unsigned wipers{1 << 13}; // startWiperList
constexpr unsigned dimmer{1 << 14}; // startDimmerList
constexpr unsigned lights{1 << 15}; // startLIGHTSLIST
constexpr unsigned compressors{1 << 16}; // startCOMPRESSORLIST
// the issection() calls of LoadFIZ_Parse()
std::array const sections{
section{"Param", '.'},
section{"Load", ':'},
section{"Dimensions", ':'},
section{"Wheels", ':'},
section{"Brake", ':'},
section{"Doors", ':'},
section{"BuffCoupl", '.'},
section{"BuffCoupl1", '.'},
section{"BuffCoupl2", '.'},
section{"TurboPos", ':'},
section{"Cntrl", '.', brake_table, ""},
section{"Headlights", ':'},
section{"Blending", ':'},
section{"DCEMUED", ':'},
section{"SpringBrake", ':'},
section{"Light", ':'},
section{"Security", ':'},
section{"Clima", ':'},
section{"Power", ':'},
section{"SpeedControl", ':'},
section{"Engine", ':'},
section{"Switches", ':'},
section{"MotorParamTable", ':', motor_table, "END-MPT"},
section{"MotorParamTable0", ':', motor_table0, "END-MPT"},
section{"Circuit", ':'},
section{"AI", ':'},
section{"RList", ':', resistors, "END-RL"},
section{"UCList", ':', universal, "END-UCL"},
section{"DList", ':', diesel, "END-DL"},
section{"DMList", ':', diesel_momentum, "END-DML"},
section{"HTCList", ':', hydro, "END-HTCL"},
section{"PmaxList", ':', diesel_pmax, "END-PML"},
section{"V2NList", ':', diesel_v2n, "END-V2NL"},
section{"ffList", ':', field, "endff"},
section{"ffBrakeList", ':', field_brake, "endff"},
section{"WWList", ':', generator, "END-WWL"},
section{"WiperList", ':', wipers, "endwl"},
section{"DimmerList", ':', dimmer, "endDimmerList"},
section{"LightsList", ':', lights, "endL"},
section{"CompressorList", ':', compressors, "endCL"},
};
// the lines closing lists, checked by the loader before the sections in this order, each closing the brake table too
struct closing {
std::string_view line;
unsigned lists;
};
std::array const closings{
closing{"END-MPT", motor_table | motor_table0},
closing{"END-RL", resistors},
closing{"END-UCL", universal},
closing{"END-DL", diesel},
closing{"END-DML", diesel_momentum},
closing{"END-V2NL", diesel_v2n},
closing{"END-HTCL", hydro},
closing{"END-PML", diesel_pmax},
closing{"endff", field | field_brake},
closing{"endwl", wipers},
closing{"endDimmerList", dimmer},
closing{"END-WWL", generator},
closing{"endL", lights},
closing{"endCL", compressors},
};
} // namespace
std::string value(std::string const &Key, std::string const &Line) {
std::string const input{" " + Line};
auto const lookup{input.find(" " + Key + "=")};
if (lookup == std::string::npos) {
return {};
}
auto const start{input.find_first_not_of(' ', lookup + Key.size() + 2)};
if (start == std::string::npos) {
return {};
}
auto const end{input.find(' ', start)};
return input.substr(start, end == std::string::npos ? std::string::npos : end - start);
}
std::vector<std::string> words(std::string_view const Line) {
std::vector<std::string> result;
std::size_t start{0};
while ((start = Line.find_first_not_of(" \t;", start)) != std::string_view::npos) {
auto const end{Line.find_first_of(" \t;", start)};
result.emplace_back(Line.substr(start, end == std::string_view::npos ? std::string_view::npos : end - start));
start = end;
}
return result;
}
section const *named(std::string_view Name) {
auto const lookup{std::ranges::find(sections, Name, &section::name)};
return lookup == sections.end() ? nullptr : std::to_address(lookup);
}
section const *opened_by(std::string_view Line) {
auto const lookup{std::ranges::find_if(sections, [&](section const &Section) { return Line.starts_with(Section.name) && Line.size() > Section.name.size() && Line[Section.name.size()] == Section.separator; })};
return lookup == sections.end() ? nullptr : std::to_address(lookup);
}
std::vector<std::string> lines(cParser &Parser) {
std::vector<std::string> result;
while (Parser.ok()) {
auto line{Parser.getToken<std::string>(false, "\n\r")};
if (line.starts_with("//") || line.find('#') != std::string::npos) {
continue;
}
line.erase(0, line.find_first_not_of(' '));
if (false == line.empty() && line.back() == '\r') {
line.pop_back();
}
result.push_back(line);
}
return result;
}
std::vector<std::string> keys(std::string const &Line) {
std::vector<std::string> result;
std::string const input{" " + Line};
for (auto equals{input.find('=')}; equals != std::string::npos; equals = input.find('=', equals + 1)) {
auto const start{input.rfind(' ', equals) + 1};
auto key{input.substr(start, equals - start)};
// a=b=c gives a, not a=b
if (false == key.empty() && key.find('=') == std::string::npos && std::ranges::find(result, key) == result.end()) {
result.push_back(std::move(key));
}
}
return result;
}
// the values the section gives, as the loader asks for them by their keys
std::string section_values(section const &Section, std::string const &Line) {
auto keys{fiz::keys(Line)};
std::ranges::sort(keys);
std::string values{Section.name};
for (auto const &key : keys) {
if (auto const value{fiz::value(key, Line)}; false == value.empty()) {
values += " " + key + "=" + value;
}
}
return values;
}
// the lists reading a row: the first one open, in the order of the loader's checks, which carries on past the dimmer list
unsigned row_readers(unsigned const Active) {
unsigned readers{0};
for (unsigned list = brake_table; list <= compressors; list <<= 1) {
if ((Active & list) == 0) {
continue;
}
readers |= list;
if (list != dimmer) {
break;
}
}
return readers;
}
std::vector<std::string> reading(std::vector<std::string> const &Lines) {
std::vector<std::string> result;
unsigned active{0};
for (auto const &line : Lines) {
if (line.empty()) {
active &= ~brake_table;
continue;
}
if (auto const close{std::ranges::find_if(closings, [&](closing const &Closing) { return line.starts_with(Closing.line); })}; close != closings.end()) {
active &= ~(close->lists | brake_table);
continue;
}
if (auto const opened{opened_by(line)}; opened != nullptr) {
active = (active & ~brake_table) | opened->list;
result.push_back(section_values(*opened, line));
continue;
}
if (auto const readers{row_readers(active)}; readers != 0) {
std::string row{std::format("row of {:x}:", readers)};
for (auto const &word : words(line)) {
row += " " + word;
}
result.push_back(std::move(row));
}
}
return result;
}
} // namespace test::fiz

44
tests/unit/fiz.h Normal file
View File

@@ -0,0 +1,44 @@
#pragma once
#include <string>
#include <string_view>
#include <vector>
class cParser;
// what the game's vehicle loader (TMoverParameters::LoadFIZ_Parse) makes of the lines of a vehicle definition,
// for the test definitions to give it the same and for checking they do
namespace test::fiz {
// a section of the definition, opened by a line starting with its name and separator. a list section is
// followed by rows, read until the line closing them (an empty line for the Cntrl brake table) and as
// long as no other list closes first, see reading()
struct section {
std::string_view name;
char separator;
unsigned list{0}; // the bit of the list its rows go to, none for a section of values only
char const *end{nullptr}; // the line closing the rows
};
// the section of the given name, or none for a name the loader doesn't know
section const *named(std::string_view Name);
// the section the line opens, or none
section const *opened_by(std::string_view Line);
// the lines of a definition as the loader takes them: without comments, leading spaces and line ends
std::vector<std::string> lines(cParser &Parser);
// the keys of the values in the line, in the order of their first appearance: the words followed by =,
// which extract_value() finds
std::vector<std::string> keys(std::string const &Line);
// the value of the key in the line, as extract_value() gives it, empty for none (where it would throw too)
std::string value(std::string const &Key, std::string const &Line);
// the words of a row, as the readers of the lists get them from cParser
std::vector<std::string> words(std::string_view Line);
// what the loader reads from the lines, in order: every section line with the values it gives, every row
// with the lists reading it. two definitions reading the same load the same vehicle
std::vector<std::string> reading(std::vector<std::string> const &Lines);
} // namespace test::fiz

393
tests/unit/golden.cpp Normal file
View File

@@ -0,0 +1,393 @@
#include "stdafx.h"
#include "golden.h"
#include "doctest.h"
#include <array>
#include <charconv>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <sstream>
#include "McZapkie/hamulce.h"
namespace test {
namespace channels {
std::vector<channel> motion() {
return {
{"V", [](auto const &M) { return M.V; }},
{"DistCounter", [](auto const &M) { return M.DistCounter; }},
{"AccS", [](auto const &M) { return M.AccS; }},
{"nrot", [](auto const &M) { return M.nrot; }},
{"Ft", [](auto const &M) { return M.Ft; }},
{"Fb", [](auto const &M) { return M.Fb; }},
{"FStand", [](auto const &M) { return M.FStand; }},
{"SlippingWheels", [](auto const &M) { return M.SlippingWheels ? 1.0 : 0.0; }},
};
}
std::vector<channel> traction() {
return {
{"Mains", [](auto const &M) { return M.Mains ? 1.0 : 0.0; }},
{"DirActive", [](auto const &M) { return M.DirActive; }},
{"MainCtrlPos", [](auto const &M) { return M.MainCtrlPos; }},
{"MainCtrlActualPos", [](auto const &M) { return M.MainCtrlActualPos; }},
{"ScndCtrlPos", [](auto const &M) { return M.ScndCtrlPos; }},
{"ScndCtrlActualPos", [](auto const &M) { return M.ScndCtrlActualPos; }},
{"Im", [](auto const &M) { return M.Im; }},
{"Itot", [](auto const &M) { return M.Itot; }},
{"EngineVoltage", [](auto const &M) { return M.EngineVoltage; }},
{"EnginePower", [](auto const &M) { return M.EnginePower; }},
{"enrot", [](auto const &M) { return M.enrot; }},
{"dizel_fill", [](auto const &M) { return M.dizel_fill; }},
{"eimv_pr", [](auto const &M) { return M.eimv_pr; }},
{"DynamicBrakeFlag", [](auto const &M) { return M.DynamicBrakeFlag ? 1.0 : 0.0; }},
};
}
std::vector<channel> brakes() {
return {
{"BrakeCtrlPos", [](auto const &M) { return M.BrakeCtrlPos; }},
{"PipePress", [](auto const &M) { return M.PipePress; }},
{"BrakePress", [](auto const &M) { return M.BrakePress; }},
{"LocBrakePress", [](auto const &M) { return M.LocBrakePress; }},
{"BRP", [](auto const &M) { return M.Hamulec->GetBRP(); }},
{"VRP", [](auto const &M) { return M.Hamulec->GetVRP(); }},
{"CRP", [](auto const &M) { return M.Hamulec->GetCRP(); }},
};
}
std::vector<channel> air() {
return {
{"Compressor", [](auto const &M) { return M.Compressor; }},
{"CompressorFlag", [](auto const &M) { return M.CompressorFlag ? 1.0 : 0.0; }},
{"ScndPipePress", [](auto const &M) { return M.ScndPipePress; }},
};
}
std::vector<channel> handles() {
std::vector<channel> result{
{"BrakeCtrlPosR", [](auto const &M) { return M.BrakeCtrlPosR; }},
{"dpMainValve", [](auto const &M) { return M.dpMainValve; }},
{"HandleCP", [](auto const &M) { return M.Handle->GetCP(); }},
{"HandleRP", [](auto const &M) { return M.Handle->GetRP(); }},
{"HandleEP", [](auto const &M) { return M.Handle->GetEP(); }},
{"LocalBrakePosA", [](auto const &M) { return M.LocalBrakePosA; }},
{"dpLocalValve", [](auto const &M) { return M.dpLocalValve; }},
};
for (int i = 0; i < 5; ++i) {
result.emplace_back(std::format("HandleSound{}", i), [i](auto const &M) { return M.Handle->GetSound(i); });
}
return result;
}
std::vector<channel> doors() {
std::vector<channel> result{
{"DoorsLocked", [](auto const &M) { return M.Doors.is_locked ? 1.0 : 0.0; }},
};
for (int side : {side::right, side::left}) {
auto const name{std::string{side == side::right ? "Right" : "Left"}};
result.emplace_back(name + "Position", [side](auto const &M) { return M.Doors.instances[side].position; });
result.emplace_back(name + "Step", [side](auto const &M) { return M.Doors.instances[side].step_position; });
result.emplace_back(name + "Open", [side](auto const &M) { return M.Doors.instances[side].is_open ? 1.0 : 0.0; });
result.emplace_back(name + "Closed", [side](auto const &M) { return M.Doors.instances[side].is_closed ? 1.0 : 0.0; });
result.emplace_back(name + "Permit", [side](auto const &M) { return M.Doors.instances[side].open_permit ? 1.0 : 0.0; });
}
return result;
}
std::vector<channel> parameters() {
std::vector<channel> result{
{"CategoryFlag", [](auto const &M) { return M.CategoryFlag; }},
{"TrainType", [](auto const &M) { return M.TrainType; }},
{"EngineType", [](auto const &M) { return static_cast<int>(M.EngineType); }},
{"Vmax", [](auto const &M) { return M.Vmax; }},
{"Mass", [](auto const &M) { return M.Mass; }},
{"TotalMass", [](auto const &M) { return M.TotalMass; }},
{"Mred", [](auto const &M) { return M.Mred; }},
{"Power", [](auto const &M) { return M.Power; }},
{"HeatingPower", [](auto const &M) { return M.HeatingPower; }},
{"LightPower", [](auto const &M) { return M.LightPower; }},
{"BatteryVoltage", [](auto const &M) { return M.BatteryVoltage; }},
{"Cx", [](auto const &M) { return M.Cx; }},
{"WheelDiameter", [](auto const &M) { return M.WheelDiameter; }},
{"TrackW", [](auto const &M) { return M.TrackW; }},
{"AxleInertialMoment", [](auto const &M) { return M.AxleInertialMoment; }},
{"NPoweredAxles", [](auto const &M) { return M.NPoweredAxles; }},
{"NAxles", [](auto const &M) { return M.NAxles; }},
{"BearingType", [](auto const &M) { return M.BearingType; }},
{"ADist", [](auto const &M) { return M.ADist; }},
{"BDist", [](auto const &M) { return M.BDist; }},
{"NBpA", [](auto const &M) { return M.NBpA; }},
{"SandCapacity", [](auto const &M) { return M.SandCapacity; }},
{"BrakeSystem", [](auto const &M) { return static_cast<int>(M.BrakeSystem); }},
{"BrakeValve", [](auto const &M) { return static_cast<int>(M.BrakeValve); }},
{"BrakeHandle", [](auto const &M) { return static_cast<int>(M.BrakeHandle); }},
{"BrakeLocHandle", [](auto const &M) { return static_cast<int>(M.BrakeLocHandle); }},
{"LocalBrake", [](auto const &M) { return static_cast<int>(M.LocalBrake); }},
{"ASBType", [](auto const &M) { return M.ASBType; }},
{"MaxBrakeForce", [](auto const &M) { return M.MaxBrakeForce; }},
{"P2FTrans", [](auto const &M) { return M.P2FTrans; }},
{"BrakeMethod", [](auto const &M) { return M.BrakeMethod; }},
{"HighPipePress", [](auto const &M) { return M.HighPipePress; }},
{"LowPipePress", [](auto const &M) { return M.LowPipePress; }},
{"DeltaPipePress", [](auto const &M) { return M.DeltaPipePress; }},
{"CntrlPipePress", [](auto const &M) { return M.CntrlPipePress; }},
{"BrakeVolume", [](auto const &M) { return M.BrakeVolume; }},
{"BrakeVVolume", [](auto const &M) { return M.BrakeVVolume; }},
{"VeselVolume", [](auto const &M) { return M.VeselVolume; }},
{"BrakeCylNo", [](auto const &M) { return M.BrakeCylNo; }},
{"BrakeCylRadius", [](auto const &M) { return M.BrakeCylRadius; }},
{"BrakeCylDist", [](auto const &M) { return M.BrakeCylDist; }},
{"BrakeCylSpring", [](auto const &M) { return M.BrakeCylSpring; }},
{"BrakeSlckAdj", [](auto const &M) { return M.BrakeSlckAdj; }},
{"BrakeRigEff", [](auto const &M) { return M.BrakeRigEff; }},
{"RapidMult", [](auto const &M) { return M.RapidMult; }},
{"RapidVel", [](auto const &M) { return M.RapidVel; }},
{"BrakeValveSize", [](auto const &M) { return M.BrakeValveSize; }},
{"MinCompressor", [](auto const &M) { return M.MinCompressor; }},
{"MaxCompressor", [](auto const &M) { return M.MaxCompressor; }},
{"CompressorSpeed", [](auto const &M) { return M.CompressorSpeed; }},
{"EmergencyValveOn", [](auto const &M) { return M.EmergencyValveOn; }},
{"AirLeakRate", [](auto const &M) { return M.AirLeakRate; }},
{"BrakeCtrlPosNo", [](auto const &M) { return M.BrakeCtrlPosNo; }},
{"MainCtrlPosNo", [](auto const &M) { return M.MainCtrlPosNo; }},
{"ScndCtrlPosNo", [](auto const &M) { return M.ScndCtrlPosNo; }},
{"LightsPosNo", [](auto const &M) { return M.LightsPosNo; }},
{"MBrake", [](auto const &M) { return M.MBrake ? 1.0 : 0.0; }},
{"StopBrakeDecc", [](auto const &M) { return M.StopBrakeDecc; }},
{"BrakeDelays", [](auto const &M) { return M.BrakeDelays; }},
{"BrakeOpModes", [](auto const &M) { return M.BrakeOpModes; }},
{"Imin", [](auto const &M) { return M.Imin; }},
{"Imax", [](auto const &M) { return M.Imax; }},
{"EIMCtrlType", [](auto const &M) { return M.EIMCtrlType; }},
{"Volume", [](auto const &M) { return M.Volume; }},
{"PantVolume", [](auto const &M) { return M.PantVolume; }},
};
for (int i = 0; i < 5; ++i) {
result.emplace_back(std::format("MaxBrakePress{}", i), [i](auto const &M) { return M.MaxBrakePress[i]; });
}
for (int i = 0; i < 3; ++i) {
result.emplace_back(std::format("BrakeCylMult{}", i), [i](auto const &M) { return M.BrakeCylMult[i]; });
}
return result;
}
std::vector<channel> operator+(std::vector<channel> Left, std::vector<channel> const &Right) {
Left.insert(Left.end(), Right.begin(), Right.end());
return Left;
}
} // namespace channels
namespace {
bool environment(char const *Name) {
auto const *value{std::getenv(Name)};
return value != nullptr && value[0] != '\0' && value[0] != '0';
}
std::string number(double Value) {
// the shortest text which reads back as the same double
std::array<char, 32> text{};
auto const result{std::to_chars(text.data(), text.data() + text.size(), Value)};
return {text.data(), result.ptr};
}
// a line of the file, whichever line ends the checkout gave it
bool read_line(std::ifstream &File, std::string &Line) {
if (false == static_cast<bool>(std::getline(File, Line))) {
return false;
}
if (false == Line.empty() && Line.back() == '\r') {
Line.pop_back();
}
return true;
}
std::vector<std::string> split(std::string const &Line) {
std::vector<std::string> fields;
std::stringstream stream(Line);
std::string field;
while (std::getline(stream, field, ',')) {
fields.push_back(field);
}
if (false == Line.empty() && Line.back() == ',') {
fields.emplace_back();
}
return fields;
}
bool same(std::string const &Recorded, std::string const &Now, bool const Exact) {
if (Recorded == Now) {
return true;
}
if (Exact) {
return false;
}
char *end{nullptr};
auto const recorded{std::strtod(Recorded.c_str(), &end)};
auto const now{std::strtod(Now.c_str(), &end)};
if (std::isnan(recorded) || std::isnan(now)) {
return std::isnan(recorded) && std::isnan(now);
}
return std::abs(recorded - now) <= 1e-9 * std::max(std::abs(recorded), std::abs(now)) + 1e-12;
}
} // namespace
trace::trace(std::string Name, std::vector<channel> Channels, int Every, bool Figures)
: m_name(std::move(Name)), m_channels(std::move(Channels)), m_every(Every), m_keep_figures(Figures) {}
void trace::sample(double Time, TMoverParameters const &Vehicle) {
// the invariants on every step, the first time each is broken
for (auto const &invariant : invariants()) {
if (std::ranges::find(m_broken, invariant.name) != m_broken.end()) {
continue;
}
if (false == invariant.holds(Vehicle, Time, m_sampled ? &m_before : nullptr)) {
m_broken.push_back(invariant.name);
m_violations.push_back(std::format("invariant \"{}\" broken in {} at {:.3f} s{}: {}", invariant.name, m_name, Time,
m_last_event.empty() ? std::string{} : std::format(", after \"{}\"", m_last_event), invariant.explain(Vehicle)));
}
}
m_before = {Time, Vehicle.V, Vehicle.DistCounter};
m_sampled = true;
m_figures.sample(Time, Vehicle);
if (m_step++ % m_every != 0) {
return;
}
// the time is a label, summed up from the steps: milliseconds are all it needs
auto row{std::format("{:.3f}", Time)};
for (auto const &channel : m_channels) {
row += "," + number(channel.value(Vehicle));
}
row += "," + m_event;
m_event.clear();
m_rows.push_back(row);
}
void trace::mark(std::string const &Event) {
m_event += (m_event.empty() ? "" : " ") + Event;
m_last_event = Event;
m_figures.mark(Event);
}
void trace::check_figures(std::filesystem::path const &Path, bool const Exact) const {
// like the samples, the key figures are recorded on the code before a change and compared after it
std::ifstream file(Path, std::ios::binary);
if (false == file.is_open()) {
if (Exact) {
FAIL_CHECK("no key figures " << Path.string() << ", run the tests with EU07_UNIT_TESTS_UPDATE_GOLDEN=1 on the code before the change to record them");
}
return;
}
std::vector<std::string> recorded;
std::string line;
while (read_line(file, line)) {
recorded.push_back(line);
}
auto const differences{figures::compare(recorded, m_figures.lines())};
if (false == differences.empty()) {
std::string text;
for (auto const &difference : differences) {
text += "\n " + difference;
}
FAIL_CHECK("key figures of " << m_name << " differ:" << text);
}
}
void trace::check() const {
for (std::size_t i = 0; i < m_violations.size(); ++i) {
if (false == excused(m_name, m_broken[i])) {
FAIL_CHECK(m_violations[i]);
}
}
std::string header{"time"};
for (auto const &channel : m_channels) {
header += "," + channel.name;
}
header += ",event";
// no spaces in the file names: the build depends on the files, and make doesn't take to them
auto name{m_name};
std::ranges::replace(name, ' ', '_');
std::filesystem::path const filename{name + ".csv"};
auto const path{std::filesystem::path{EU07_UNIT_TESTS_DATA} / "golden" / filename};
auto figurespath{path};
figurespath.replace_extension(".figures");
if (environment("EU07_UNIT_TESTS_UPDATE_GOLDEN")) {
record(path, figurespath, header);
return;
}
// the samples aren't kept in the repository, they're recorded on the code before a change and compared after it
auto const exact{environment("EU07_UNIT_TESTS_GOLDEN_EXACT")};
if (m_keep_figures) {
check_figures(figurespath, exact);
}
check_samples(path, header, exact);
}
void trace::record(std::filesystem::path const &Path, std::filesystem::path const &FiguresPath, std::string const &Header) const {
std::filesystem::create_directories(Path.parent_path());
std::ofstream file(Path, std::ios::binary);
file << Header << "\n";
for (auto const &row : m_rows) {
file << row << "\n";
}
if (m_keep_figures) {
std::ofstream figuresfile(FiguresPath, std::ios::binary);
for (auto const &line : m_figures.lines()) {
figuresfile << line << "\n";
}
}
MESSAGE("recorded the golden trace " << Path.string());
}
void trace::check_samples(std::filesystem::path const &path, std::string const &header, bool const exact) const {
std::ifstream file(path, std::ios::binary);
if (false == file.is_open()) {
if (exact) {
FAIL_CHECK("no golden trace " << path.string() << ", run the tests with EU07_UNIT_TESTS_UPDATE_GOLDEN=1 on the code before the change to record it");
}
return;
}
std::string line;
read_line(file, line);
if (line != header) {
FAIL_CHECK("golden trace " << m_name << " has other channels: " << line << ", the test records " << header);
return;
}
auto const names{split(header)};
std::size_t row{0};
while (read_line(file, line)) {
if (row >= m_rows.size()) {
FAIL_CHECK("golden trace " << m_name << " is longer: " << m_rows.size() << " rows now, more recorded");
return;
}
auto const recorded{split(line)};
auto const now{split(m_rows[row])};
for (std::size_t field = 0; field < names.size(); ++field) {
auto const recordedvalue{field < recorded.size() ? recorded[field] : std::string{}};
auto const nowvalue{field < now.size() ? now[field] : std::string{}};
if (false == same(recordedvalue, nowvalue, exact || names[field] == "event")) {
FAIL_CHECK("golden trace " << m_name << " differs first at time " << now.front() << " in " << names[field] << ": " << nowvalue
<< " now, " << recordedvalue << " recorded\n now: " << m_rows[row] << "\n recorded: " << line);
return;
}
}
++row;
}
if (row != m_rows.size()) {
FAIL_CHECK("golden trace " << m_name << " is shorter: " << m_rows.size() << " rows now, " << row << " recorded");
}
}
} // namespace test

90
tests/unit/golden.h Normal file
View File

@@ -0,0 +1,90 @@
#pragma once
#include <filesystem>
#include <functional>
#include <string>
#include <vector>
#include "McZapkie/MOVER.h"
#include "figures.h"
#include "invariants.h"
namespace test {
// one value of the vehicle a trace follows
struct channel {
std::string name;
std::function<double(TMoverParameters const &)> value;
};
// sets of channels for the usual subjects of a test, which can be put together
namespace channels {
std::vector<channel> motion(); // speed, distance, forces
std::vector<channel> traction(); // controllers, current, voltage, engine
std::vector<channel> brakes(); // pressures of the brake pipe, cylinders and reservoirs
std::vector<channel> air(); // compressor and main reservoir
std::vector<channel> handles(); // the driver's brake handle and the local brake handle, with their sounds
std::vector<channel> doors(); // both sides' doors and steps, their permits and the lock
std::vector<channel> parameters(); // what the vehicle definition sets, as the loader read it
std::vector<channel> operator+(std::vector<channel> Left, std::vector<channel> const &Right);
} // namespace channels
// characterisation of the simulation: the values of the channels over a run of a vehicle, compared with the
// ones the code gave when the trace got recorded, in tests/unit/data/golden/<Name>.csv (spaces as underscores). a difference fails the
// test, naming the channel and the time it shows up first. the samples are tens of megabytes and only mean
// something against the code before a change, so they stay out of the repository: running the tests with
// EU07_UNIT_TESTS_UPDATE_GOLDEN=1 records them on the code before the change, and after it they're compared.
// a trace with nothing recorded checks only the invariants and its test's own CHECKs, as the CI does.
//
// values are compared within a relative tolerance of 1e-9 (the CI builds with GCC and glibc, whose maths
// functions can differ from MSVC's in the last bits), or exactly with EU07_UNIT_TESTS_GOLDEN_EXACT=1, which is the check to
// run for a change which shouldn't alter anything at all, and which fails a trace with nothing recorded.
//
// besides the samples, a trace records the key figures of the run (figures.h) in <Name>.figures, the few
// lines a person reads of what a change altered, and checks the invariants (invariants.h) on every step
//
// test::trace trace{"eu07 notch up", test::channels::motion() + test::channels::traction()};
// eu07.record(trace);
// ... run the vehicle ...
// trace.check();
class trace {
public:
// samples every Every-th step of the vehicle: 100, a second at the usual step, is plenty for a run of minutes,
// as a difference stays once the simulation has taken it in
// Figures: whether to keep the key figures, which a trace sampled by hand, at times of its own, has no use for
trace(std::string Name, std::vector<channel> Channels, int Every = 10, bool Figures = true);
// called by the vehicle after every step
void sample(double Time, TMoverParameters const &Vehicle);
// adds an event of the test to the trace, so a difference can be told apart from the moment of the action
void mark(std::string const &Event);
// compares the trace and its key figures (figures.h) with the recorded ones, or records them, and reports the
// invariants broken
void check() const;
// the invariants broken in the trace (see invariants.h), the first time each, excused or not
std::vector<std::string> const &violations() const { return m_violations; }
private:
void check_figures(std::filesystem::path const &Path, bool Exact) const;
// writes the samples and the key figures, as recorded on the code before a change
void record(std::filesystem::path const &Path, std::filesystem::path const &FiguresPath, std::string const &Header) const;
// compares the samples with the recorded ones
void check_samples(std::filesystem::path const &path, std::string const &header, bool exact) const;
std::string m_name;
std::vector<channel> m_channels;
int m_every;
long m_step{0};
std::vector<std::string> m_rows; // formatted, as in the file
std::string m_event; // marked since the last row
std::string m_last_event; // the last one marked, for the invariants broken after it
snapshot m_before{};
bool m_sampled{false};
std::vector<std::string> m_broken; // the names of the invariants broken so far
std::vector<std::string> m_violations;
bool m_keep_figures;
figures m_figures;
};
} // namespace test

163
tests/unit/invariants.cpp Normal file
View File

@@ -0,0 +1,163 @@
#include "stdafx.h"
#include "invariants.h"
#include <cmath>
#include <cstdio>
#include <initializer_list>
#include <utility>
namespace test {
namespace {
std::string values(std::initializer_list<std::pair<char const *, double>> Values) {
std::string result;
for (auto const &[name, value] : Values) {
result += std::format("{}{}={:.6g}", result.empty() ? "" : " ", name, value);
}
return result;
}
bool electric(TMoverParameters const &M) {
return M.EngineType == TEngineType::ElectricSeriesMotor || M.EngineType == TEngineType::ElectricInductionMotor;
}
double max_brake_press(TMoverParameters const &M) {
auto result{0.0};
for (auto const press : M.MaxBrakePress) {
result = std::max(result, press);
}
return result;
}
// the margin over a limit which a step of the simulation may overshoot it by
constexpr double pressure_margin{0.05};
// the highest acceleration of a vehicle, 5 g, which a collision goes well over
constexpr double highest_acceleration{5.0 * 9.81};
} // namespace
std::vector<invariant> const &invariants() {
static std::vector<invariant> const rules{
{"every value a number",
[](auto const &M, double, auto const *) {
std::initializer_list<double> const values{M.V, M.AccS, M.Ft, M.Fb, M.Im, M.Itot, M.EngineVoltage,
M.enrot, M.PipePress, M.BrakePress, M.LocBrakePress, M.ScndPipePress, M.Compressor, M.BatteryVoltage};
return std::ranges::all_of(values, [](double const Value) { return std::isfinite(Value); });
},
[](auto const &M) {
return values({{"V", M.V}, {"AccS", M.AccS}, {"Ft", M.Ft}, {"Fb", M.Fb}, {"Im", M.Im}, {"Itot", M.Itot}, {"EngineVoltage", M.EngineVoltage},
{"enrot", M.enrot}, {"PipePress", M.PipePress}, {"BrakePress", M.BrakePress}, {"LocBrakePress", M.LocBrakePress},
{"ScndPipePress", M.ScndPipePress}, {"Compressor", M.Compressor}, {"BatteryVoltage", M.BatteryVoltage}});
}},
{"brake cylinders between empty and the vehicle's highest pressure",
[](auto const &M, double, auto const *) {
// a step overshoots the setting a little: the EU07's LSt fills its cylinders to 4.0 bar of its 3.8
auto const limit{1.1 * max_brake_press(M) + pressure_margin};
return M.BrakePress >= -pressure_margin && M.BrakePress <= limit && M.LocBrakePress >= -pressure_margin && M.LocBrakePress <= limit;
},
[](auto const &M) { return values({{"BrakePress", M.BrakePress}, {"LocBrakePress", M.LocBrakePress}, {"max(MaxBrakePress)", max_brake_press(M)}}); }},
{"pipes between empty and the highest governor setting",
[](auto const &M, double, auto const *) {
// all the air comes from the main reservoirs, which the game sets up to 10 bar, some (the Impuls) to 12;
// the driver's valve in its filling position joins the brake pipe to them
auto const highest{1.01 * std::max(10.0, M.MaxCompressor) + pressure_margin};
std::initializer_list<double> const pressures{M.PipePress, M.ScndPipePress, M.Compressor};
return std::ranges::all_of(pressures, [highest](double const Press) { return Press >= -pressure_margin && Press <= highest; });
},
[](auto const &M) { return values({{"PipePress", M.PipePress}, {"ScndPipePress", M.ScndPipePress}, {"Compressor", M.Compressor}}); }},
{"main reservoir no higher than the compressor's governor",
[](auto const &M, double, auto const *) { return M.MaxCompressor <= 0.0 || M.Compressor <= 1.01 * M.MaxCompressor + pressure_margin; },
[](auto const &M) { return values({{"Compressor", M.Compressor}, {"MaxCompressor", M.MaxCompressor}}); }},
{"no motor current with the line breaker open",
[](auto const &M, double, auto const *) { return M.Mains || false == electric(M) || std::abs(M.Im) < 1.0; },
[](auto const &M) { return values({{"Mains", M.Mains}, {"Im", M.Im}, {"Itot", M.Itot}}); }},
{"no motor current with the line contactors open, but for the dynamic brake",
[](auto const &M, double, auto const *) {
// the dynamic brake closes its own circuit of the motors and the resistors
return M.StLinFlag || M.DynamicBrakeFlag || M.EngineType != TEngineType::ElectricSeriesMotor || std::abs(M.Im) < 1.0;
},
[](auto const &M) {
return values({{"StLinFlag", M.StLinFlag}, {"DynamicBrakeFlag", M.DynamicBrakeFlag}, {"Im", M.Im}, {"MainCtrlActualPos", M.MainCtrlActualPos}});
}},
{"no tractive force without power",
[](auto const &M, double, auto const *) { return M.Power >= 0.01 || std::abs(M.Ft) < 1.0; },
[](auto const &M) { return values({{"Power", M.Power}, {"Ft", M.Ft}}); }},
{"engine fill between none and full",
[](auto const &M, double, auto const *) { return M.dizel_fill >= 0.0 && M.dizel_fill <= 1.0; },
[](auto const &M) { return values({{"dizel_fill", M.dizel_fill}}); }},
{"diesel engine turning forward, no faster than its top speed",
[](auto const &M, double, auto const *) {
// the governor lets it overshoot a little (the SN61 by 1%)
return M.EngineType != TEngineType::DieselEngine || (M.enrot >= 0.0 && M.enrot <= 1.05 * M.dizel_nmax);
},
[](auto const &M) { return values({{"enrot", M.enrot}, {"dizel_nmax", M.dizel_nmax}}); }},
{"battery no higher than its nominal voltage",
[](auto const &M, double, auto const *) { return M.NominalBatteryVoltage <= 0.0 || M.BatteryVoltage <= 1.05 * M.NominalBatteryVoltage; },
[](auto const &M) { return values({{"BatteryVoltage", M.BatteryVoltage}, {"NominalBatteryVoltage", M.NominalBatteryVoltage}}); }},
{"distance counted as the speed runs",
[](auto const &M, double Time, auto const *Before) {
if (Before == nullptr) {
return true;
}
// the speed of the step is between the speeds at its ends, or a bit off it, as the movement is integrated;
// through a standstill, down to none
auto const dt{Time - Before->time};
auto const covered{(M.DistCounter - Before->DistCounter) * 1000.0};
auto const lowest{M.V * Before->V > 0.0 ? std::min(std::abs(M.V), std::abs(Before->V)) : 0.0};
auto const highest{std::max(std::abs(M.V), std::abs(Before->V))};
return covered >= lowest * dt * 0.9 - 0.01 && covered <= highest * dt * 1.1 + 0.01;
},
[](auto const &M) { return values({{"V", M.V}, {"DistCounter", M.DistCounter}}); }},
{"no acceleration over 5 g",
[](auto const &M, double Time, auto const *Before) {
return Before == nullptr || std::abs(M.V - Before->V) <= highest_acceleration * (Time - Before->time) + 0.01;
},
[](auto const &M) { return values({{"V", M.V}, {"AccS", M.AccS}}); }},
{"hand brake applied, the wheels braked on the move",
[](auto const &M, double, auto const *) { return false == M.MBrake || M.ManualBrakePos == 0 || std::abs(M.V) < 0.5 || std::abs(M.Fb) > 0.0; },
[](auto const &M) { return values({{"ManualBrakePos", M.ManualBrakePos}, {"V", M.V}, {"Fb", M.Fb}}); }},
};
return rules;
}
namespace {
struct excuse {
char const *trace; // a part of the trace's name
char const *invariant;
char const *reason;
};
std::vector<excuse> const excuses{
// what the tests do
{"couplers/collision/", "no acceleration over 5 g", "vehicles run into each other"},
{"controls/collision/", "no acceleration over 5 g", "vehicles run into each other"},
{"couplers/collision/", "distance counted as the speed runs", "the buffers throw the vehicle back within a step"},
{"switches_live/", "no acceleration over 5 g", "sampled by hand, a second apart in the trace for 2 s run"},
{"switches_live/", "distance counted as the speed runs", "sampled by hand, a second apart in the trace for 2 s run"},
{"brake handles/", "main reservoir no higher than the compressor's governor", "the test holds the main reservoir at 8 bar"},
{"local brake/", "main reservoir no higher than the compressor's governor", "the test holds the main reservoir at 8 bar"},
{"brake cycle/at speed/", "brake cylinders between empty and the vehicle's highest pressure", "the rapid step of the valves at speed"},
// the vehicles as they are
{"elf/2", "main reservoir no higher than the compressor's governor",
"the units' reservoirs share the supply pipe: the A car's governor stops at 9 bar, the C car's at 8.5"},
{"sm03_v1/", "brake cylinders between empty and the vehicle's highest pressure",
"its K valve fills the cylinders to the auxiliary reservoir's pressure (4.27 bar), over MaxBP 3.5"},
{"eu04_v1/", "brake cylinders between empty and the vehicle's highest pressure",
"its K valve fills the cylinders to the auxiliary reservoir's pressure (3.19 bar), over MaxBP 2.8"},
{"sn61", "brake cylinders between empty and the vehicle's highest pressure",
"its ESt3d_PZZ valve fills the cylinders to the auxiliary reservoir's pressure in an emergency (4.6 bar), over MaxBP 3.8"},
{"sm03_v1/", "diesel engine turning forward, no faster than its top speed", "a lower gear at speed drives the engine over its top speed, by 10%"},
{"wmb10_v1/", "diesel engine turning forward, no faster than its top speed", "a lower gear at speed drives the engine over its top speed, by 25%"},
{"controls/full physics/", "no acceleration over 5 g", "the couplers' slack taken up as the train starts throws the wagons about at up to 5.7 g"},
{"controls/full physics/", "distance counted as the speed runs", "the couplers' slack taken up as the train starts throws the wagons back within a step"},
};
} // namespace
bool excused(std::string_view const Trace, std::string_view const Invariant) {
return std::ranges::any_of(excuses, [&](excuse const &Excuse) { return Excuse.invariant == Invariant && Trace.find(Excuse.trace) != std::string_view::npos; });
}
} // namespace test

36
tests/unit/invariants.h Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#include <functional>
#include <string>
#include <string_view>
#include <vector>
#include "McZapkie/MOVER.h"
namespace test {
// what the rules over time need of the step before
struct snapshot {
double time;
double V;
double DistCounter;
};
// a rule of physics or of the vehicle's circuits, which holds for every vehicle after every step, whatever
// the test does with it. every golden trace checks them as it samples (see golden.h), on every step, not only
// on the recorded ones, and with no comparison to a recorded file, so they hold on any compiler. a change which
// alters the behaviour on purpose records the traces anew, and the rules still check the new behaviour makes sense.
struct invariant {
std::string name;
// Before is null at the first step of a trace
std::function<bool(TMoverParameters const &Now, double Time, snapshot const *Before)> holds;
// the values the rule looks at, shown when it's broken
std::function<std::string(TMoverParameters const &Now)> explain;
};
std::vector<invariant> const &invariants();
// the traces where the game breaks a rule, as it is, with the reason (see COVERAGE.md, what the tests found)
bool excused(std::string_view Trace, std::string_view Invariant);
} // namespace test

View File

@@ -2,7 +2,7 @@
#include "stdafx.h"
#include "doctest.h"
#include "utilities/Globals.h"
#include "vehicle.h"
namespace {
@@ -13,8 +13,8 @@ class deterministic_random : public doctest::IReporter {
public:
explicit deterministic_random(doctest::ContextOptions const &) {}
void test_case_start(doctest::TestCaseData const &) override { reseed(); }
void test_case_reenter(doctest::TestCaseData const &) override { reseed(); }
void test_case_start(doctest::TestCaseData const &) override { test::reseed_random(); }
void test_case_reenter(doctest::TestCaseData const &) override { test::reseed_random(); }
// the rest of doctest's reporter interface: a listener reports nothing, the console reporter does that
void report_query(doctest::QueryData const &) override { /* nothing to report */ }
@@ -27,12 +27,6 @@ public:
void log_assert(doctest::AssertData const &) override { /* nothing to report */ }
void log_message(doctest::MessageData const &) override { /* nothing to report */ }
void test_case_skipped(doctest::TestCaseData const &) override { /* nothing to report */ }
private:
static void reseed() {
Global.random_engine.seed(1);
Global.local_random_engine.seed(1);
}
};
} // namespace

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include <vector>
namespace test {
// the real vehicle definitions of data/fiz, as paths for definition::fiz(): every .json file there, sorted.
// vehicles the game defines by including others are vehicles of their own too (201e-rn, included by 201e-w)
std::vector<std::string> const &real_vehicles();
} // namespace test

80
tests/unit/scenario.cpp Normal file
View File

@@ -0,0 +1,80 @@
#include "stdafx.h"
#include "scenario.h"
#include "McZapkie/hamulce.h"
namespace test {
scenario::scenario(std::string Name, std::vector<channel> Channels, track const &Track) : m_name(std::move(Name)), m_channels(std::move(Channels)), m_track(Track) {
reseed_random();
}
scenario &scenario::add(std::string const &Path, int Cab, int Coupling, double Velocity, double Gap) {
m_train.add(definition::fiz(Path), Cab, Coupling, Velocity, Gap);
return *this;
}
void scenario::record() {
// on the first action, once the train is complete
if (false == m_traces.empty()) {
return;
}
for (std::size_t i = 0; i < m_train.size(); ++i) {
m_traces.push_back(std::make_unique<trace>(std::format("{}/{}", m_name, i), m_channels, 100));
m_train[i].record(*m_traces.back());
}
}
void scenario::act(std::string const &Event) {
record();
for (auto const &trace : m_traces) {
trace->mark(Event);
}
}
void scenario::wait(double Seconds) {
record();
drive(driver(), m_track, Seconds);
}
bool scenario::start_up() {
record();
return test::start_up(driver(), m_track, 300.0, m_traces.front().get());
}
void scenario::get_going() {
act("get going");
release_brakes(cab());
for (int i = 0; i < 3 && cab().DirActive <= 0; ++i) {
cab().DirectionForward();
}
wait(20.0);
}
void scenario::full_power(double Seconds) {
auto &controlling{driver().controlling};
controlling.IncMainCtrl(1);
for (int i = 0; i < 90 && false == controlling.StLinFlag; ++i) {
wait(1.0);
}
for (int i = 0; i < 60 && false == controlling.IsMainCtrlMaxPowerPos(); ++i) {
controlling.IncMainCtrl(1);
wait(1.0);
}
wait(Seconds);
}
void scenario::notch_back(int Position) {
auto &controlling{driver().controlling};
while (controlling.MainCtrlPos > Position && controlling.DecMainCtrl(1)) {
wait(1.0);
}
}
void scenario::check() const {
for (auto const &trace : m_traces) {
trace->check();
}
}
} // namespace test

63
tests/unit/scenario.h Normal file
View File

@@ -0,0 +1,63 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "consist.h"
#include "drive.h"
#include "golden.h"
namespace test {
// a scenario of a test: a train of real vehicles (one vehicle is a train too), a driver in the front one,
// and a golden trace for each vehicle (data/golden/<Name>/<number of the vehicle>.csv) with the events of
// the scenario in them:
// test::scenario scenario{"doors/elf", test::channels::motion() + test::channels::doors()};
// scenario.add("elf_v1/21we-a", 1).add("elf_v1/21we-b").add("elf_v1/21we-c");
// scenario.start_up();
// scenario.act("open left");
// scenario.cab().OperateDoors(side::left, true);
// scenario.wait(10.0);
// scenario.check();
class scenario {
public:
scenario(std::string Name, std::vector<channel> Channels, track const &Track = {0.15, 0.0, catenary_3kv});
// adds a real vehicle at the back, see consist::add()
scenario &add(std::string const &Path, int Cab = 0, int Coupling = consist::full_coupling, double Velocity = 0.0, double Gap = 0.0);
consist &train() { return m_train; }
TMoverParameters &vehicle(std::size_t Index) { return m_train[Index].mover(); }
TMoverParameters &cab() { return m_train[0].mover(); }
driven driver() { return driving(m_train); }
track &line() { return m_track; }
// marks the event in the traces, the action follows it
void act(std::string const &Event);
// runs the train with the driver attending to it
void wait(double Seconds);
// gets the train ready to run, see test::start_up(); returns whether it did
bool start_up();
// releases the brakes and sets the direction forward, for a train to move off
void get_going();
// the main controller up to its full power position, a notch a second, and Seconds more there. the line
// contactors close at the first position, after their delay (InitialCtrlDelay) and once the pressure switch
// lets them (the cylinders under 2 bar, the pipe over 3.6), so it waits there for them, as the AI does
void full_power(double Seconds);
// the main controller back to the given position, a notch a second
void notch_back(int Position);
// checks the traces
void check() const;
private:
void record();
std::string m_name;
std::vector<channel> m_channels;
track m_track;
consist m_train;
std::vector<std::unique_ptr<trace>> m_traces;
};
} // namespace test

View File

@@ -0,0 +1,112 @@
#include "stdafx.h"
#include "doctest.h"
#include "scenario.h"
#include "drive.h"
// characterisation of the auxiliary circuits of vehicles: the battery drained and charged, and the air of the
// pantographs kept in their own tank, whose pressure switch opens the line breaker, each in a scenario on real
// vehicles recorded in golden traces (data/golden/auxiliaries/)
using test::scenario;
using namespace test::channels;
namespace {
std::vector<test::channel> auxiliaries() {
return {
{"BatteryVoltage", [](auto const &M) { return M.BatteryVoltage; }},
{"Battery", [](auto const &M) { return M.Battery ? 1.0 : 0.0; }},
{"ConverterFlag", [](auto const &M) { return M.ConverterFlag ? 1.0 : 0.0; }},
{"Power110vIsAvailable", [](auto const &M) { return M.Power110vIsAvailable ? 1.0 : 0.0; }},
{"PantPress", [](auto const &M) { return M.PantPress; }},
{"PantVolume", [](auto const &M) { return M.PantVolume; }},
{"PantCompFlag", [](auto const &M) { return M.PantCompFlag ? 1.0 : 0.0; }},
{"PantPressSwitchActive", [](auto const &M) { return M.PantPressSwitchActive ? 1.0 : 0.0; }},
{"PantPressLockActive", [](auto const &M) { return M.PantPressLockActive ? 1.0 : 0.0; }},
};
}
} // namespace
TEST_CASE("auxiliaries: the battery drained by the lights and the pantographs' compressor, until nothing starts") {
// without the converter the battery falls quickly to 90 V, then slowly as the lights and the small compressor
// draw on it; at 70 V (110 / 1.57) it opens the line breaker, and the locomotive doesn't start again
scenario scenario{"auxiliaries/battery/eu07 drained", traction() + auxiliaries()};
scenario.add("4e_v1/4e", 1);
REQUIRE(scenario.start_up());
auto &eu07{scenario.cab()};
REQUIRE(eu07.BatteryVoltage == doctest::Approx(eu07.NominalBatteryVoltage));
scenario.act("converter off, lights on, small compressor on");
eu07.ConverterSwitch(false);
eu07.iLights[end::front] = 1 | 2 | 4;
eu07.iLights[end::rear] = 16;
eu07.PantCompFlag = true;
scenario.wait(30.0);
CHECK(eu07.BatteryVoltage < 0.83 * eu07.NominalBatteryVoltage);
CHECK(eu07.Mains);
scenario.wait(5000.0);
CHECK(eu07.BatteryVoltage < eu07.NominalBatteryVoltage / 1.57);
CHECK_FALSE(eu07.Mains);
scenario.act("start up again");
CHECK_FALSE(scenario.start_up());
CHECK(eu07.BatteryVoltage < eu07.NominalBatteryVoltage / 1.57);
scenario.check();
}
TEST_CASE("auxiliaries: the battery switched off, and charged by the converter") {
scenario scenario{"auxiliaries/battery/eu07 switched off", traction() + auxiliaries()};
scenario.add("4e_v1/4e", 1);
REQUIRE(scenario.start_up());
auto &eu07{scenario.cab()};
scenario.act("converter off");
eu07.ConverterSwitch(false);
scenario.wait(10.0);
auto const discharged{eu07.BatteryVoltage};
CHECK(discharged < 0.83 * eu07.NominalBatteryVoltage);
// the converter charges it back at once
scenario.act("converter on");
eu07.ConverterSwitch(true);
scenario.wait(10.0);
CHECK(eu07.BatteryVoltage == doctest::Approx(eu07.NominalBatteryVoltage));
// switched off, the battery falls to 90 V at once, then holds
scenario.act("battery off");
eu07.MainSwitch(false);
eu07.ConverterSwitch(false);
eu07.BatterySwitch(false);
scenario.wait(10.0);
CHECK(eu07.BatteryVoltage == doctest::Approx(discharged).epsilon(0.01));
scenario.check();
}
TEST_CASE("auxiliaries: the pantographs on their own tank, leaking until the pressure switch opens the line breaker") {
// the pantographs cut off the main reservoir (bPantKurek3 off) are held up by the air of their own small tank,
// which leaks; under MinPress the pressure switch, primed at 4.6 bar, opens the line breaker. the small
// compressor pumps the tank up to 4.8 bar, priming the switch again. (an electric unit's switch turns its
// converter off instead; the EW58's pantographs have an automatic valve to the main reservoir, which keeps
// their tank up)
scenario scenario{"auxiliaries/pantograph tank/eu07", traction() + auxiliaries()};
scenario.add("4e_v1/4e", 1);
REQUIRE(scenario.start_up());
auto &eu07{scenario.cab()};
// started up on the small compressor, the tank gets joined to the main reservoir, which primes the switch
scenario.act("tank joined to the main reservoir");
eu07.bPantKurek3 = true;
scenario.wait(30.0);
REQUIRE(eu07.PantPressSwitchActive);
scenario.act("tank cut off the main reservoir");
eu07.bPantKurek3 = false;
eu07.PantCompFlag = false;
scenario.wait(600.0);
CHECK(eu07.PantPress < eu07.EnginePowerSource.CollectorParameters.MinPress);
CHECK_FALSE(eu07.PantPressSwitchActive);
CHECK_FALSE(eu07.Mains);
scenario.act("small compressor on");
eu07.PantCompFlag = true;
for (int i = 0; i < 60 && eu07.PantCompFlag; ++i) {
scenario.wait(10.0);
}
CHECK_FALSE(eu07.PantCompFlag);
CHECK(eu07.PantPress >= 4.6); // stopped at 4.8, leaking since
CHECK(eu07.PantPressSwitchActive);
scenario.check();
}

View File

@@ -0,0 +1,194 @@
#include "stdafx.h"
#include "doctest.h"
#include "vehicle.h"
#include "golden.h"
#include "real_vehicles.h"
#include "McZapkie/hamulce.h"
#include <array>
#include <map>
using test::definition;
using test::vehicle;
// characterisation of the brakes of every real vehicle: the brake pipe driven through a whole cycle, as the
// locomotive of a train would drive it, and the distributor valve, the cylinders and the reservoirs followed
// in golden traces (data/golden/brake cycle/). pressures in bar, the pipe runs at 5.0
namespace {
struct stage {
char const *name;
double pipe; // held brake pipe pressure
double seconds;
};
// charging, the service application in steps, release, the overcharge used to speed the release up and its
// drop back, the emergency application and recharging after it
std::array<stage, 9> const cycle{{
{"charge", 5.0, 90.0}, {"light", 4.6, 20.0}, {"medium", 4.2, 20.0}, {"full service", 3.5, 20.0},
{"release", 5.0, 40.0}, {"overcharge", 5.4, 15.0}, {"back to 5", 5.0, 45.0}, {"emergency", 0.0, 15.0},
{"recharge", 5.0, 90.0},
}};
// returns the pressure in the cylinders at the end of each stage, by the name of the stage
std::map<std::string, double, std::less<>> run_cycle(vehicle &Vehicle, test::trace &Trace) {
std::map<std::string, double, std::less<>> cylinders;
for (auto const &stage : cycle) {
Trace.mark(stage.name);
Vehicle.hold_brake_pipe(stage.pipe);
Vehicle.run(stage.seconds);
cylinders[stage.name] = Vehicle->BrakePress;
}
return cylinders;
}
} // namespace
TEST_CASE("brake cycle: every real vehicle through charge, service steps, release, overcharge and emergency") {
for (auto const &path : test::real_vehicles()) {
CAPTURE(path);
test::reseed_random();
vehicle real{definition::fiz(path)};
test::trace trace{"brake cycle/" + path, test::channels::brakes(), 100};
real.record(trace);
// a vehicle braked by the pipe: released once charged, braking harder by every step of the service
// application, and by the emergency one. the release isn't checked: some vehicles don't release within
// its 40 s, others keep a little air after the overcharge
if (auto cylinders{run_cycle(real, trace)}; cylinders["full service"] > 0.0) {
CHECK(cylinders["charge"] < 0.1);
CHECK(cylinders["light"] <= cylinders["medium"]);
CHECK(cylinders["medium"] <= cylinders["full service"] + 0.01);
CHECK(cylinders["full service"] > 1.0);
CHECK(cylinders["emergency"] > 0.5);
}
else {
// the draisine, the road vehicle and the catenary car, which have no brake worked by the pipe
CHECK(cylinders["emergency"] == 0.0);
}
trace.check();
}
}
TEST_CASE("brake cycle: every brake delay setting of the real vehicles") {
// G, P, R and their mixes change how fast the cylinders fill and empty; every vehicle offering more
// than one setting goes through the service part of the cycle on each
for (auto const &path : test::real_vehicles()) {
CAPTURE(path);
test::reseed_random();
vehicle probe{definition::fiz(path)};
for (auto const delay : {bdelay_G, bdelay_P, bdelay_R, bdelay_R + bdelay_M, bdelay_G + bdelay_M}) {
if ((probe->BrakeDelays & delay) != delay || delay == probe->BrakeDelayFlag) {
continue;
}
CAPTURE(delay);
test::reseed_random();
vehicle real{definition::fiz(path)};
REQUIRE(real->BrakeDelaySwitch(delay));
test::trace trace{std::format("brake delays/{} {}", path, delay), test::channels::brakes(), 100};
real.record(trace);
std::map<std::string, double, std::less<>> cylinders;
for (auto const &stage : {stage{"charge", 5.0, 90.0}, stage{"full service", 3.5, 30.0}, stage{"release", 5.0, 60.0}}) {
trace.mark(stage.name);
real.hold_brake_pipe(stage.pipe);
real.run(stage.seconds);
cylinders[stage.name] = real->BrakePress;
}
// every setting brakes in half a minute and releases in a minute
CHECK(cylinders["full service"] > 1.0);
CHECK(cylinders["release"] < 0.3);
trace.check();
}
}
}
TEST_CASE("brake cycle: the valves the game knows but none of its vehicles has, fitted to a wagon") {
// the variants of the Westinghouse and Knorr valves, the Hik ones, the old ESt3 and the CV1 ones, with a name
// the game doesn't know at the end. any other name with ESt in it is an Oerlikon valve (NESt3) whose name sets
// its relays: -ED the electrodynamic brake's one, AL2 the load-weighing one, without ESt3 the rapid one
for (auto const *name : {"W_Lu_L", "W_Lu_XR", "W_Lu_VI", "Kg", "Kp", "Kss", "Kkg", "Kkp", "Kks", "Hikp1", "Hikss", "Hikg1", "SW", "NESt3", "M483", "CV1", "CV1_L_TR",
"CV1_R", "ESt4-ED", "ESt4AL2", "no such valve"}) {
CAPTURE(name);
test::reseed_random();
vehicle real{definition::fiz("3wl_v2/3wl").set("Brake", "BrakeValve", name)};
test::trace trace{std::string{"brake cycle/fitted/"} + name, test::channels::brakes(), 100};
real.record(trace);
run_cycle(real, trace);
trace.check();
}
}
TEST_CASE("brake cycle: the rapid setting at speed, the anti-slip brake and the releaser button") {
// R gives the cylinders more air at speed (RM); the anti-slip request the game makes while the wheels slip and
// slow down (3) and the releaser button (ub_Release) let the LSt's cylinders out, the KE's not
struct valve {
char const *name;
char const *path;
int delay;
double service; // in the cylinders, full service over 100 km/h
bool releases; // by the button
};
for (auto const &[name, path, delay, service, releases] : {valve{"KE", "z1_v2/152a", bdelay_R, 3.78, false}, valve{"KE in P", "z1_v2/152a", bdelay_P, 2.70, false},
valve{"LSt", "4e_v1/4e", bdelay_R, 5.79, true}, valve{"LSt in P", "4e_v1/4e", bdelay_P, 3.68, true}}) {
CAPTURE(name);
test::reseed_random();
vehicle real{definition::fiz(path), 160.0};
real->DecManualBrakeLevel(ManualBrakePosNo);
real->BrakeDelaySwitch(delay); // false when already set
REQUIRE(real->BrakeDelayFlag == delay);
using test::channels::operator+;
test::trace trace{std::string{"brake cycle/at speed/"} + name, test::channels::motion() + test::channels::brakes(), 100};
real.record(trace);
trace.mark("charge");
real.hold_brake_pipe(5.0);
real.run(60.0);
trace.mark("full service");
real.hold_brake_pipe(3.5);
real.run(10.0);
CHECK(real->Vel > 100.0);
CHECK(real->BrakePress == doctest::Approx(service).epsilon(0.01));
trace.mark("anti-slip");
real->Hamulec->ASB(3);
real.run(2.0);
real->Hamulec->ASB(0);
real.run(3.0);
trace.mark("releaser button");
real->Hamulec->SetUniversalFlag(TUniversalBrake::ub_Release);
real.run(3.0);
real->Hamulec->SetUniversalFlag(0);
CHECK((real->BrakePress < 1.0) == releases);
// released for good, the pipe still at full service
real.run(40.0);
CHECK((real->BrakePress < 0.1) == releases);
trace.check();
}
}
TEST_CASE("brake cycle: every material of the brake shoes and disks, braking a wagon from speed") {
// the friction of the shoes (BM) against the speed and the pressing force (friction.cpp): the game's wagons
// have P10-Bg, P10-Bgu, Cosid and the disks; FR513, FR510, P10yBg and P10yBgu none of them, nor an unknown one
// (P10, as with no BM). Mg adds the magnetic rail brake under 3 bar in the pipe
for (auto const *material : {"P10-Bg", "P10-Bgu", "FR513", "FR510", "Cosid", "P10yBg", "P10yBgu", "Disk1", "Disk1+Mg", "Disk2", "no such material"}) {
CAPTURE(material);
test::reseed_random();
vehicle real{definition::fiz("3wl_v2/3wl").set("Brake", "BM", material), 100.0};
real->DecManualBrakeLevel(ManualBrakePosNo);
using test::channels::operator+;
test::trace trace{std::string{"brake cycle/materials/"} + material, test::channels::motion() + test::channels::brakes(), 100};
real.record(trace);
trace.mark("charge");
real.hold_brake_pipe(5.0);
real.run(30.0);
auto const speed{real->Vel};
trace.mark("full service");
real.hold_brake_pipe(3.5);
real.run(20.0);
CHECK(real->Vel < speed - 10.0);
CHECK(real->Fb > 0.0);
trace.mark("emergency");
real.hold_brake_pipe(0.0);
real.run(60.0);
trace.check();
}
}

View File

@@ -0,0 +1,154 @@
#include "stdafx.h"
#include "doctest.h"
#include "vehicle.h"
#include "golden.h"
#include "real_vehicles.h"
#include "drive.h"
#include "McZapkie/hamulce.h"
using test::definition;
using test::vehicle;
// characterisation of the driver's brake handles and the local brake handles of the real locomotives and
// units: every position of the handle, with the main reservoir kept full, followed in golden traces
// (data/golden/brake handles/). the vehicle runs alone, so the handle drives only its own brake pipe
namespace {
std::vector<test::channel> handle_channels() {
using namespace test::channels;
return brakes() + handles() + air();
}
// the vehicles with a driver's brake handle
bool has_handle(vehicle &Vehicle) {
return Vehicle->BrakeCtrlPosNo > 1;
}
void hold(vehicle &Vehicle, test::trace &Trace, std::string const &Event, double Position, double Seconds) {
Trace.mark(Event);
Vehicle->BrakeLevelSet(Position);
Vehicle.run(Seconds);
}
// every position of the driver's handle, recorded in the trace
void every_position(vehicle &Vehicle, test::trace &Trace) {
Vehicle.hold_main_reservoir(std::max(Vehicle->MaxCompressor, 8.0));
auto &handle{*Vehicle->Handle};
Vehicle.record(Trace);
hold(Vehicle, Trace, "running", handle.GetPos(bh_RP), 90.0);
// every position from the lowest to the highest, emergency included, then back to running
auto const lowest{static_cast<int>(std::ceil(handle.GetPos(bh_MIN)))};
auto const highest{static_cast<int>(std::floor(handle.GetPos(bh_MAX)))};
for (auto position = lowest; position <= highest; ++position) {
hold(Vehicle, Trace, std::format("position {}", position), position, 15.0);
hold(Vehicle, Trace, "running", handle.GetPos(bh_RP), 30.0);
}
// filling stroke, neutral and the positions of the ep brake, where the handle has them
hold(Vehicle, Trace, "filling stroke", handle.GetPos(bh_FS), 10.0);
hold(Vehicle, Trace, "neutral", handle.GetPos(bh_NP), 20.0);
hold(Vehicle, Trace, "first step", handle.GetPos(bh_MB), 20.0);
hold(Vehicle, Trace, "full service", handle.GetPos(bh_FB), 20.0);
hold(Vehicle, Trace, "ep release", handle.GetPos(bh_EPR), 10.0);
hold(Vehicle, Trace, "ep hold", handle.GetPos(bh_EPN), 10.0);
hold(Vehicle, Trace, "ep brake", handle.GetPos(bh_EPB), 10.0);
hold(Vehicle, Trace, "running", handle.GetPos(bh_RP), 60.0);
}
} // namespace
TEST_CASE("brake handles: every position of the driver's handle of every real vehicle") {
for (auto const &path : test::real_vehicles()) {
CAPTURE(path);
test::reseed_random();
vehicle real{definition::fiz(path)};
if (false == has_handle(real)) {
continue;
}
test::trace trace{"brake handles/" + path, handle_channels(), 100};
every_position(real, trace);
trace.check();
}
}
TEST_CASE("brake handles: the handles the game knows but none of its vehicles has, fitted to an EU07") {
// test is a handle for trying things out, D2 and Westinghouse fall back to the plain handle of the base class
for (auto const *name : {"test", "D2", "Westinghouse", "MHZ_EN57", "St113", "no such handle"}) {
CAPTURE(name);
test::reseed_random();
vehicle real{definition::fiz("4e_v1/4e").set("Cntrl", "BrakeHandle", name)};
test::trace trace{std::string{"brake handles/fitted/"} + name, handle_channels(), 100};
every_position(real, trace);
trace.check();
}
}
TEST_CASE("brake handles: the local brake handle of every real vehicle") {
for (auto const &path : test::real_vehicles()) {
CAPTURE(path);
test::reseed_random();
vehicle real{definition::fiz(path)};
if (false == has_handle(real)) {
continue;
}
real.hold_main_reservoir(std::max(real->MaxCompressor, 8.0));
test::trace trace{"local brake/" + path, handle_channels(), 100};
real.record(trace);
real->BrakeLevelSet(real->Handle->GetPos(bh_RP));
trace.mark("charge");
real.run(60.0);
for (auto const position : {0.25, 0.5, 1.0, 0.5, 0.0}) {
trace.mark(std::format("local {:f}", position));
real->LocalBrakePosA = position;
real.run(15.0);
}
trace.check();
}
}
TEST_CASE("brake handles: the overload and the high pressure buttons of the MHZ handles") {
// the overload button charges the pipe over its running pressure (TP), which then decays; the high pressure
// button gives the filling stroke its overpressure, where the handle has one only with the button pressed.
// from the cab, with the battery on and the driver attending to the vigilance: the electro-pneumatic handles
// work only so
// the MHZ_EN57, whose unit doesn't charge its pipe alone, fitted to an EU07
for (auto const &[name, definition] : std::vector<std::pair<std::string, test::definition>>{
{"eu47", definition::fiz("e186_v2/eu47")},
{"e6act", definition::fiz("e6act_v2/e6acta")},
{"eu07 with MHZ_EN57", definition::fiz("4e_v1/4e").set("Cntrl", "BrakeHandle", "MHZ_EN57")}}) {
CAPTURE(name);
test::reseed_random();
vehicle real{definition, 0.0, "test", 1};
real->BatterySwitch(true);
real->CabActivisation();
real.hold_main_reservoir(std::max(real->MaxCompressor, 8.0));
test::trace trace{"brake handles/buttons/" + name, handle_channels(), 100};
real.record(trace);
auto &handle{*real->Handle};
auto const at{[&trace, &real](std::string const &Event, double Position, double Seconds) {
trace.mark(Event);
real->BrakeLevelSet(Position);
test::drive(test::driving(real), {}, Seconds);
}};
at("running", handle.GetPos(bh_RP), 60.0);
auto const running{real->PipePress};
handle.SetUniversalFlag(TUniversalBrake::ub_Overload);
at("overload button", handle.GetPos(bh_RP), 30.0);
handle.SetUniversalFlag(0);
auto const overloaded{real->PipePress};
at("overload decaying", handle.GetPos(bh_RP), 240.0);
auto const decayed{real->PipePress};
handle.SetUniversalFlag(TUniversalBrake::ub_HighPressure);
at("high pressure button, filling stroke", handle.GetPos(bh_FS), 10.0);
auto const filled{real->PipePress};
handle.SetUniversalFlag(0);
at("running", handle.GetPos(bh_RP), 120.0);
CHECK(running == doctest::Approx(5.0).epsilon(0.01));
CHECK(overloaded > running + 0.3);
CHECK(decayed < overloaded);
CHECK(filled >= decayed - 0.05);
trace.check();
}
}

229
tests/unit/test_cab.cpp Normal file
View File

@@ -0,0 +1,229 @@
#include "stdafx.h"
#include "doctest.h"
#include "consist.h"
#include "scenario.h"
#include "drive.h"
#include "McZapkie/hamulce.h"
#include "utilities/utilities.h"
// characterisation of what the cab (Train.cpp) and the AI (Driver.cpp, driverhints.cpp) call on a vehicle
// besides the controls of test_controls.cpp: the gauges and lamps reading other vehicles of the train, the
// controller of the electrodynamic brake apart from the local brake, the spring brake, the ep brake's fuse,
// cutting off damaged motors, the cab activated by itself, a road vehicle's place across the road, and the
// lamps and the buzzer of the security system
using test::consist;
using test::definition;
using test::scenario;
using test::vehicle;
using namespace test::channels;
TEST_CASE("cab: the engine rotation of up to three vehicles in multiple, on the tachometers of the SN61") {
consist train;
auto &first{train.add(definition::fiz("sn61_v2/sn61"), 1).mover()};
auto &second{train.add(definition::fiz("sn61_v2/sn61")).mover()};
auto &third{train.add(definition::fiz("sn61_v2/sn61")).mover()};
first.enrot = -10.0;
second.enrot = 20.0;
third.enrot = 30.0;
CHECK(first.ShowEngineRotation(1) == doctest::Approx(10.0));
CHECK(first.ShowEngineRotation(2) == doctest::Approx(20.0));
CHECK(first.ShowEngineRotation(3) == doctest::Approx(30.0));
CHECK(first.ShowEngineRotation(4) == doctest::Approx(0.0));
// the others look the same way along their couplers, front first
CHECK(second.ShowEngineRotation(2) == doctest::Approx(10.0));
CHECK(third.ShowEngineRotation(2) == doctest::Approx(20.0));
CHECK(third.ShowEngineRotation(3) == doctest::Approx(10.0));
// no control cable, no reading
consist apart;
auto const &alone{apart.add(definition::fiz("sn61_v2/sn61"), 1).mover()};
apart.add(definition::fiz("sn61_v2/sn61"), 0, coupling::coupler | coupling::brakehose);
CHECK(alone.ShowEngineRotation(2) == doctest::Approx(0.0));
}
TEST_CASE("cab: the lamps of the overload relay and the starting resistors, read through the control cable") {
consist unit;
auto &trailer{unit.add(definition::fiz("ed72_v1/5bs-ra"), 1).mover()};
auto &motor{unit.add(definition::fiz("ed72_v1/6bs-sa")).mover()};
REQUIRE(trailer.Power < 0.01);
REQUIRE(motor.Power > 0.01);
CHECK_FALSE(trailer.FuseFlagCheck());
CHECK_FALSE(trailer.ResistorsFlagCheck());
motor.FuseFlag = true;
motor.ResistorsFlag = true;
CHECK(trailer.FuseFlagCheck());
CHECK(trailer.ResistorsFlagCheck());
CHECK(motor.FuseFlagCheck());
CHECK(motor.ResistorsFlagCheck());
}
TEST_CASE("cab: the engine's idle speed, and the ET42's second controller shown in its dynamic brake") {
vehicle sn61{definition::fiz("sn61_v2/sn61")};
CHECK(sn61->EngineIdleRPM() == doctest::Approx(sn61->dizel_nmin * 60.0));
vehicle su45{definition::fiz("su45_v2/301d")};
CHECK(su45->EngineIdleRPM() == doctest::Approx(su45->DElist[su45->MainCtrlNoPowerPos()].RPM));
vehicle eu07{definition::fiz("4e_v1/4e")};
CHECK(eu07->EngineIdleRPM() == std::numeric_limits<double>::max());
vehicle et42{definition::fiz("et42_v2/112e-a")};
CHECK(et42->GetVirtualScndPos() == 0);
et42->DynamicBrakeFlag = true;
CHECK(et42->GetVirtualScndPos() == -1);
et42->ScndCtrlPos = 1;
CHECK(et42->GetVirtualScndPos() == 1);
// with a speed control in place of the second controller, either end of it, for the AI
vehicle railbus{definition::fiz("21xm_v1/214ma")};
REQUIRE(railbus->SpeedCtrl);
railbus->ScndCtrlPos = 1;
CHECK(railbus->IsScndCtrlNoPowerPos());
CHECK(railbus->IsScndCtrlMaxPowerPos());
CHECK(eu07->IsScndCtrlNoPowerPos());
CHECK_FALSE(eu07->IsScndCtrlMaxPowerPos());
}
TEST_CASE("cab: the controller of the electrodynamic brake apart from the local brake") {
// none of the game's vehicles has one yet (SplitEDPneumaticBrake): the electrodynamic brake follows its own
// controller of DBPN positions, the local brake is pneumatic alone
scenario scenario{"cab/split electrodynamic brake", motion() + traction() + brakes()};
scenario.add("e186_v2/eu47", 1, consist::full_coupling, 100.0).add("bdhpumn_v3/bdhpumn", 0, consist::full_coupling, 100.0);
auto &eu47{scenario.cab()};
CHECK_FALSE(eu47.IncDynamicBrakeLevel(1.f));
CHECK(eu47.DynamicBrakeRatio() == doctest::Approx(0.0));
scenario.train()[0].mover().SplitEDPneumaticBrake = true;
REQUIRE(scenario.start_up());
REQUIRE(eu47.DynamicBrakeCtrlPosNo == 10);
CHECK(eu47.DynamicBrakeAvailable());
scenario.act("dynamic brake up");
for (int i = 0; i < 5; ++i) {
CHECK(eu47.IncDynamicBrakeLevel(1.f));
}
CHECK(eu47.DynamicBrakeRatio() == doctest::Approx(0.5));
scenario.wait(10.0);
CHECK(eu47.DynamicBrakeFlag);
CHECK(eu47.Im < -50.0);
CHECK(eu47.BrakePress < 0.1);
scenario.act("dynamic brake full");
CHECK(eu47.DynamicBrakeLevelSet(2.0));
CHECK(eu47.DynamicBrakeRatio() == doctest::Approx(1.0));
CHECK_FALSE(eu47.IncDynamicBrakeLevel(1.f));
scenario.wait(10.0);
scenario.act("dynamic brake off");
for (int i = 0; i < 20 && eu47.DecDynamicBrakeLevel(1.f); ++i) {
// down a notch at a time
}
CHECK(eu47.DynamicBrakeRatio() == doctest::Approx(0.0));
// the local brake no longer brings the motors in
scenario.act("local brake");
eu47.IncLocalBrakeLevel(LocalBrakePosNo);
scenario.wait(5.0);
CHECK(eu47.BrakePress > 1.0);
scenario.check();
}
TEST_CASE("cab: the spring brake shut off and released by hand, the ep brake's fuse, the train brake handle stepped") {
scenario scenario{"cab/214m spring brake", motion() + brakes()};
scenario.add("21xm_v1/214ma", 1);
REQUIRE(scenario.start_up());
auto &railbus{scenario.cab()};
scenario.act("spring brake on");
railbus.SpringBrakeActivate(true);
scenario.wait(10.0);
CHECK(railbus.SpringBrake.IsActive);
// released by hand only with its cylinder empty of the air holding it off
auto const pressure{railbus.SpringBrake.Cylinder->P()};
CHECK(railbus.SpringBrakeRelease() == (pressure < railbus.SpringBrake.MinForcePressure));
scenario.act("shut off");
CHECK(railbus.SpringBrakeShutOff(true));
CHECK_FALSE(railbus.SpringBrakeShutOff(true));
scenario.wait(5.0);
CHECK(railbus.SpringBrakeShutOff(false));
scenario.act("spring brake off");
railbus.SpringBrakeActivate(false);
scenario.wait(10.0);
// the ep brake's fuse
CHECK(railbus.EpFuseSwitch(false));
CHECK_FALSE(railbus.EpFuseSwitch(false));
CHECK(railbus.EpFuseSwitch(true));
// the train brake handle stepped as the AI does, false at its ends
scenario.act("handle stepped");
railbus.BrakeLevelSet(railbus.Handle->GetPos(bh_RP));
CHECK(railbus.BrakeLevelAdd(1.0));
CHECK(railbus.BrakeCtrlPos == static_cast<int>(railbus.Handle->GetPos(bh_RP)) + 1);
CHECK(railbus.BrakeLevelAdd(-1.0));
CHECK_FALSE(railbus.BrakeLevelAdd(-10.0));
CHECK_FALSE(railbus.BrakeLevelAdd(100.0));
scenario.wait(5.0);
scenario.check();
}
TEST_CASE("cab: damaged motors cut off in a car without a driver") {
// cutting off takes the damage off the engine and half of the powered axles with it; only in a car nobody
// drives, with its engine damaged
vehicle driven{definition::fiz("ew58_v1/ew58-sa"), 0.0, "test", 1};
driven->BatterySwitch(true);
REQUIRE(driven->CabActivisation());
driven->DamageFlag |= dtrain_engine;
CHECK_FALSE(driven->CutOffEngine());
vehicle car{definition::fiz("ew58_v1/ew58-sa")};
auto const axles{car->NPoweredAxles};
CHECK_FALSE(car->CutOffEngine());
car->DamageFlag |= dtrain_engine;
CHECK(car->CutOffEngine());
CHECK(car->NPoweredAxles == axles / 2);
CHECK_FALSE(TestFlag(car->DamageFlag, dtrain_engine));
vehicle wagon{definition::fiz("3wl_v2/3wl")};
wagon->DamageFlag |= dtrain_engine;
CHECK_FALSE(wagon->CutOffEngine());
}
TEST_CASE("cab: activated by itself, where the vehicle allows it") {
for (auto const allowed : {false, true}) {
CAPTURE(allowed);
vehicle eu07{definition::fiz("4e_v1/4e").set("Cntrl", "AutomaticCabActivation", allowed ? "Yes" : "No"), 0.0, "test", 1};
eu07->BatterySwitch(true);
CHECK(eu07->CabActivisationAuto() == allowed);
CHECK((eu07->CabActive != 0) == allowed);
CHECK(eu07->CabDeactivisationAuto() == allowed);
CHECK(eu07->CabActive == 0);
}
}
TEST_CASE("cab: a road vehicle moved across its road") {
vehicle bus{definition::fiz("road/man/nl293")};
bus.step(test::track{0.15, 0.0, 0.0, 2, 1, 7.0});
REQUIRE(TestFlag(bus->CategoryFlag, 2));
CHECK(bus->ChangeOffsetH(0.5));
CHECK(bus->OffsetTrackH == doctest::Approx(0.5));
// off the edge of the road
CHECK_FALSE(bus->ChangeOffsetH(100.0));
vehicle eu07{definition::fiz("4e_v1/4e")};
CHECK_FALSE(eu07->ChangeOffsetH(0.5));
}
TEST_CASE("cab: the lamps and the buzzer of the security system left unattended") {
scenario scenario{"cab/security system", motion() + brakes()};
scenario.add("4e_v1/4e", 1);
REQUIRE(scenario.start_up());
auto &eu07{scenario.cab()};
scenario.get_going();
scenario.full_power(10.0);
auto const &security{eu07.SecuritySystem};
CHECK_FALSE(security.is_beeping());
scenario.act("unattended");
// the train runs with nobody acknowledging the vigilance
bool blinked{false};
bool beeped{false};
for (int i = 0; i < 120; ++i) {
scenario.train().run(1.0, scenario.line());
blinked |= security.is_vigilance_blinking();
beeped |= security.is_beeping();
}
CHECK(blinked);
CHECK(beeped);
CHECK_FALSE(security.is_cabsignal_beeping());
CHECK(eu07.PipePress < 1.0);
CHECK(security.radiostop_available());
scenario.check();
}

View File

@@ -0,0 +1,547 @@
#include "stdafx.h"
#include "doctest.h"
#include "vehicle.h"
#include "golden.h"
#include "consist.h"
#include "real_vehicles.h"
#include <format>
#include <functional>
#include "McZapkie/hamulce.h"
// characterisation of the commands vehicles pass each other along the couplers (RunCommand): what each of them
// does to every real vehicle, recorded in golden traces (data/golden/commands/), how they travel through a train of
// vehicles coupled either way round, and the loading and unloading driven by them
using test::definition;
using test::vehicle;
namespace {
double flag(bool Value) {
return Value ? 1.0 : 0.0;
}
// what the commands change
std::vector<test::channel> state() {
std::vector<test::channel> result{
{"MainCtrlPos", [](auto const &M) { return M.MainCtrlPos; }},
{"ScndCtrlPos", [](auto const &M) { return M.ScndCtrlPos; }},
{"LastRelayTime", [](auto const &M) { return M.LastRelayTime; }},
{"eimic_real", [](auto const &M) { return M.eimic_real; }},
{"TUHEX_StageActual", [](auto const &M) { return M.TUHEX_StageActual; }},
{"TUHEX_Active", [](auto const &M) { return flag(M.TUHEX_Active); }},
{"DynamicBrakeEMUStatus", [](auto const &M) { return flag(M.DynamicBrakeEMUStatus); }},
{"BrakeReleaser", [](auto const &M) { return flag(M.Hamulec->Releaser()); }},
{"WaterPumpBreaker", [](auto const &M) { return flag(M.WaterPump.breaker); }},
{"WaterPumpEnabled", [](auto const &M) { return flag(M.WaterPump.is_enabled); }},
{"WaterPumpDisabled", [](auto const &M) { return flag(M.WaterPump.is_disabled); }},
{"WaterHeaterBreaker", [](auto const &M) { return flag(M.WaterHeater.breaker); }},
{"WaterHeaterEnabled", [](auto const &M) { return flag(M.WaterHeater.is_enabled); }},
{"WaterCircuitsLink", [](auto const &M) { return flag(M.WaterCircuitsLink); }},
{"FuelPumpEnabled", [](auto const &M) { return flag(M.FuelPump.is_enabled); }},
{"FuelPumpDisabled", [](auto const &M) { return flag(M.FuelPump.is_disabled); }},
{"OilPumpEnabled", [](auto const &M) { return flag(M.OilPump.is_enabled); }},
{"OilPumpDisabled", [](auto const &M) { return flag(M.OilPump.is_disabled); }},
{"CompartmentLightsEnabled", [](auto const &M) { return flag(M.CompartmentLights.is_enabled); }},
{"CompartmentLightsDisabled", [](auto const &M) { return flag(M.CompartmentLights.is_disabled); }},
{"MainSwitch", [](auto const &M) { return flag(M.Mains); }},
{"Heating", [](auto const &M) { return flag(M.Heating); }},
{"DirActive", [](auto const &M) { return M.DirActive; }},
{"DirAbsolute", [](auto const &M) { return M.DirAbsolute; }},
{"CabActive", [](auto const &M) { return M.CabActive; }},
{"CabMaster", [](auto const &M) { return flag(M.CabMaster); }},
{"AutoRelayFlag", [](auto const &M) { return flag(M.AutoRelayFlag); }},
{"ConverterAllow", [](auto const &M) { return flag(M.ConverterAllow); }},
{"Battery", [](auto const &M) { return flag(M.Battery); }},
{"CompressorAllow", [](auto const &M) { return flag(M.CompressorAllow); }},
{"CompressorListPos", [](auto const &M) { return M.CompressorListPos; }},
{"DoorLock", [](auto const &M) { return flag(M.Doors.lock_enabled); }},
{"DoorStep", [](auto const &M) { return flag(M.Doors.step_enabled); }},
{"DoorRemoteOnly", [](auto const &M) { return flag(M.Doors.remote_only); }},
{"DepartureSignal", [](auto const &M) { return flag(M.DepartureSignal); }},
{"PantsValve", [](auto const &M) { return flag(M.PantsValve.is_enabled) + 2 * flag(M.PantsValve.is_disabled); }},
{"MaxCurrentHigh", [](auto const &M) { return flag(M.MotorOverloadRelayHighThreshold); }},
{"Imin", [](auto const &M) { return M.Imin; }},
{"DamageFlag", [](auto const &M) { return M.DamageFlag; }},
{"RadioStop", [](auto const &M) { return flag(M.RadioStopFlag); }},
{"BrakeDelayFlag", [](auto const &M) { return M.BrakeDelayFlag; }},
{"SandDose", [](auto const &M) { return flag(M.SandDose); }},
{"CabSignal", [](auto const &M) { return flag(M.SecuritySystem.is_cabsignal_blinking()); }},
{"LoadAmount", [](auto const &M) { return M.LoadAmount; }},
{"LoadStatus", [](auto const &M) { return M.LoadStatus; }},
{"SpeedCtrlValue", [](auto const &M) { return M.SpeedCtrlValue; }},
{"SpeedCtrlParking", [](auto const &M) { return flag(M.SpeedCtrlUnit.Parking); }},
{"SpringBrakeActivate", [](auto const &M) { return flag(M.SpringBrake.Activate); }},
};
for (int end : {end::front, end::rear}) {
auto const name{std::string{end == end::front ? "Front" : "Rear"}};
result.emplace_back("MotorBlowers" + name + "Enabled", [end](auto const &M) { return flag(M.MotorBlowers[end].is_enabled); });
result.emplace_back("MotorBlowers" + name + "Disabled", [end](auto const &M) { return flag(M.MotorBlowers[end].is_disabled); });
result.emplace_back("Pant" + name + "Valve", [end](auto const &M) { return flag(M.Pantographs[end].valve.is_enabled) + 2 * flag(M.Pantographs[end].valve.is_disabled); });
}
for (int side : {side::right, side::left}) {
auto const name{std::string{side == side::right ? "Right" : "Left"}};
result.emplace_back(name + "DoorPermit", [side](auto const &M) { return flag(M.Doors.instances[side].open_permit); });
result.emplace_back(name + "DoorRemoteOpen", [side](auto const &M) { return flag(M.Doors.instances[side].remote_open); });
result.emplace_back(name + "DoorRemoteClose", [side](auto const &M) { return flag(M.Doors.instances[side].remote_close); });
}
return result;
}
struct command {
std::string name;
double value1;
double value2{1.0}; // the direction it comes from, 1 the front
};
// every command RunCommand knows, with values taking each of its ways, and one it doesn't
std::vector<command> const commands{
{"MainCtrl", 1},
{"MainCtrl", 2},
{"MainCtrl", 0},
{"MainCtrl", 99},
{"ScndCtrl", 1},
{"ScndCtrl", 0},
{"ScndCtrl", 99},
{"EIMIC", 0.5},
{"EIMIC", -0.5},
{"Brake", 1},
{"Brake", 2},
{"Brake", 0},
{"BrakeReleaser", 1},
{"BrakeReleaser", 0},
{"WaterPumpBreakerSwitch", 1},
{"WaterPumpSwitch", 1},
{"WaterPumpSwitchOff", 1},
{"WaterPumpSwitchOff", 0},
{"WaterPumpSwitch", 0},
{"WaterPumpBreakerSwitch", 0},
{"WaterHeaterBreakerSwitch", 1},
{"WaterHeaterSwitch", 1},
{"WaterHeaterSwitch", 0},
{"WaterHeaterBreakerSwitch", 0},
{"WaterCircuitsLinkSwitch", 1},
{"WaterCircuitsLinkSwitch", 0},
{"FuelPumpSwitch", 1},
{"FuelPumpSwitchOff", 1},
{"FuelPumpSwitchOff", 0},
{"FuelPumpSwitch", 0},
{"OilPumpSwitch", 1},
{"OilPumpSwitchOff", 1},
{"OilPumpSwitchOff", 0},
{"OilPumpSwitch", 0},
{"MotorBlowersFrontSwitch", 1},
{"MotorBlowersFrontSwitchOff", 1},
{"MotorBlowersFrontSwitchOff", 0},
{"MotorBlowersFrontSwitch", 0},
{"MotorBlowersRearSwitch", 1},
{"MotorBlowersRearSwitchOff", 1},
{"MotorBlowersRearSwitchOff", 0},
{"MotorBlowersRearSwitch", 0},
{"CompartmentLightsSwitch", 1},
{"CompartmentLightsSwitchOff", 1},
{"CompartmentLightsSwitchOff", 0},
{"CompartmentLightsSwitch", 0},
{"BatterySwitch", 1},
{"ConverterSwitch", 1},
{"CompressorSwitch", 1},
{"CompressorPreset", 1},
{"CompressorPreset", 99},
{"CompressorPreset", -1},
{"MainSwitch", 1},
{"HeatingSwitch", 1},
{"HeatingSwitch", 0},
{"Direction", 1},
{"Direction", -1},
{"Direction", 0},
{"CabActivisation", 1, 1},
{"CabActivisation", 1, -1},
{"CabActivisation", 0, 1},
{"AutoRelaySwitch", 1},
{"AutoRelaySwitch", 0},
{"RelayReset", 1},
{"DoorPermit", 1, 1},
{"DoorPermit", 2, 1},
{"DoorPermit", -3, 1},
{"DoorPermit", 1, -1},
{"DoorPermit", 3, -1},
{"DoorOpen", 1, 1},
{"DoorOpen", 2, -1},
{"DoorClose", 3, 1},
{"DoorOpen", 3, 1},
{"DoorClose", 1, -1},
{"DoorLock", 0},
{"DoorLock", 1},
{"DoorStep", 0},
{"DoorStep", 1},
{"DoorMode", 1},
{"DoorMode", 0},
{"DepartureSignal", 1},
{"DepartureSignal", 0},
{"PantValve", 1},
{"PantValve", 0x80 | 1},
{"PantValve", 0x40 | 1, -1},
{"PantValve", 0x80 | 0x40 | 2, -1},
{"PantValve", 3},
{"PantValve", 4},
{"PantValve", 0x80 | 5},
{"PantValve", 0x80 | 6},
{"PantsValve", 1},
{"PantsValve", 2},
{"PantsValve", 3},
{"PantsValve", 4},
{"PantsValve", 5},
{"PantsValve", 6},
{"PantAllDown", 1},
{"PantAllDown", 0},
{"MaxCurrentSwitch", 1},
{"MaxCurrentSwitch", 0},
{"MinCurrentSwitch", 1},
{"MinCurrentSwitch", 0},
{"SetDamage", 4, 1},
{"SetDamage", 4, -1},
{"SetDamage", 4, 0},
{"Emergency_brake", 1},
{"Emergency_brake", 0},
{"BrakeDelay", 1},
{"BrakeDelay", 2},
{"BrakeDelay", 4},
{"BrakeDelay", 8},
{"BrakeDelay", 5},
{"Sandbox", 1},
{"Sandbox", 0},
{"CabSignal", 0},
{"Load=coal", 5, 0},
{"Load=Coal", 5, 20},
{"UnLoad=coal", 5, 0},
{"UnLoad=Coal", 5, 0},
{"SpeedCntrl", 40.4},
{"SpeedCtrlUnit.Parking", 1},
{"SpeedCtrlUnit.Parking", 0},
{"SpringBrakeActivate", 1},
{"SpringBrakeActivate", 0},
{"MainSwitch", 0},
{"CompressorSwitch", 0},
{"ConverterSwitch", 0},
{"BatterySwitch", 0},
{"SpringBrakeActivate", 1},
{"NoSuchCommand", 1},
};
} // namespace
TEST_CASE("commands: every command run on every real vehicle") {
for (auto const &path : test::real_vehicles()) {
CAPTURE(path);
test::reseed_random();
vehicle real{definition::fiz(path), 0.0, "test", 1};
// loading takes place where the command was given, which comes with the command (PutCommand). a
// vehicle gets none before its first one, TCommand::Location is left uninitialised
real->CommandIn.Location = real->Loc;
auto done{false};
auto channels{state()};
channels.emplace_back("Done", [&done](auto const &) { return flag(done); });
test::trace trace{"commands/" + path, channels, 1, false};
trace.sample(0.0, real.mover());
for (std::size_t i = 0; i < commands.size(); ++i) {
auto const &command{commands[i]};
trace.mark(std::format("{} {} {}", command.name, command.value1, command.value2));
done = real->RunCommand(command.name, command.value1, command.value2, coupling::control);
trace.sample(static_cast<double>(i + 1), real.mover());
}
trace.check();
}
}
namespace {
// a switch of the cab, worked on the driven vehicle
struct action {
std::string name;
std::function<bool(TMoverParameters &)> work;
};
// every switch sending its state to the rest of the train, each way and to each range, in the order a crew
// could work them, with the controllers and the direction among them to see what they make of the state
std::vector<action> const actions{
{"CabActivisation", [](auto &M) { return M.CabActivisation(); }},
{"BatterySwitch on", [](auto &M) { return M.BatterySwitch(true, range_t::consist); }},
{"CompartmentLightsSwitch on", [](auto &M) { return M.CompartmentLightsSwitch(true, range_t::consist); }},
{"CompartmentLightsSwitchOff on", [](auto &M) { return M.CompartmentLightsSwitchOff(true, range_t::consist); }},
{"CompartmentLightsSwitchOff off", [](auto &M) { return M.CompartmentLightsSwitchOff(false, range_t::unit); }},
{"WaterPumpBreakerSwitch on", [](auto &M) { return M.WaterPumpBreakerSwitch(true, range_t::consist); }},
{"WaterPumpSwitch on", [](auto &M) { return M.WaterPumpSwitch(true, range_t::consist); }},
{"WaterPumpSwitchOff on", [](auto &M) { return M.WaterPumpSwitchOff(true, range_t::unit); }},
{"WaterPumpSwitchOff off", [](auto &M) { return M.WaterPumpSwitchOff(false, range_t::consist); }},
{"WaterHeaterBreakerSwitch on", [](auto &M) { return M.WaterHeaterBreakerSwitch(true, range_t::consist); }},
{"WaterHeaterSwitch on", [](auto &M) { return M.WaterHeaterSwitch(true, range_t::consist); }},
{"WaterCircuitsLinkSwitch on", [](auto &M) { return M.WaterCircuitsLinkSwitch(true, range_t::consist); }},
{"FuelPumpSwitch on", [](auto &M) { return M.FuelPumpSwitch(true, range_t::consist); }},
{"FuelPumpSwitchOff on", [](auto &M) { return M.FuelPumpSwitchOff(true, range_t::unit); }},
{"FuelPumpSwitchOff off", [](auto &M) { return M.FuelPumpSwitchOff(false, range_t::consist); }},
{"OilPumpSwitch on", [](auto &M) { return M.OilPumpSwitch(true, range_t::consist); }},
{"OilPumpSwitchOff on", [](auto &M) { return M.OilPumpSwitchOff(true, range_t::unit); }},
{"OilPumpSwitchOff off", [](auto &M) { return M.OilPumpSwitchOff(false, range_t::consist); }},
{"MotorBlowersSwitch front on", [](auto &M) { return M.MotorBlowersSwitch(true, end::front, range_t::consist); }},
{"MotorBlowersSwitch rear on", [](auto &M) { return M.MotorBlowersSwitch(true, end::rear, range_t::unit); }},
{"MotorBlowersSwitchOff front on", [](auto &M) { return M.MotorBlowersSwitchOff(true, end::front, range_t::consist); }},
{"MotorBlowersSwitchOff rear on", [](auto &M) { return M.MotorBlowersSwitchOff(true, end::rear, range_t::unit); }},
{"MotorBlowersSwitchOff front off", [](auto &M) { return M.MotorBlowersSwitchOff(false, end::front, range_t::consist); }},
{"MotorBlowersSwitchOff rear off", [](auto &M) { return M.MotorBlowersSwitchOff(false, end::rear, range_t::consist); }},
{"OperatePantographsValve enable", [](auto &M) { return M.OperatePantographsValve(operation_t::enable, range_t::consist); }},
{"OperatePantographValve front enable", [](auto &M) { return M.OperatePantographValve(end::front, operation_t::enable, range_t::consist); }},
{"OperatePantographValve rear enable_on", [](auto &M) { return M.OperatePantographValve(end::rear, operation_t::enable_on, range_t::unit); }},
{"OperatePantographValve rear enable_off", [](auto &M) { return M.OperatePantographValve(end::rear, operation_t::enable_off, range_t::unit); }},
{"DirectionForward", [](auto &M) { return M.DirectionForward(); }},
{"ConverterSwitch on", [](auto &M) { return M.ConverterSwitch(true, range_t::consist); }},
{"CompressorSwitch on", [](auto &M) { return M.CompressorSwitch(true, range_t::consist); }},
{"ChangeCompressorPreset 1", [](auto &M) { return M.ChangeCompressorPreset(1, range_t::consist); }},
{"FuseOn", [](auto &M) { return M.FuseOn(range_t::consist); }},
{"RelayReset all", [](auto &M) { return M.RelayReset(0xff, range_t::consist); }},
{"UniversalResetButton 1", [](auto &M) { return M.UniversalResetButton(0, range_t::consist); }},
{"UniversalResetButton 2", [](auto &M) { return M.UniversalResetButton(1, range_t::unit); }},
{"MainSwitch on", [](auto &M) { return M.MainSwitch(true, range_t::consist); }},
{"HeatingSwitch on", [](auto &M) { return M.HeatingSwitch(true, range_t::consist); }},
{"MaxCurrentSwitch on", [](auto &M) { return M.MaxCurrentSwitch(true, range_t::consist); }},
{"MinCurrentSwitch on", [](auto &M) { return M.MinCurrentSwitch(true); }},
{"CurrentSwitch on", [](auto &M) { return M.CurrentSwitch(true); }},
{"AutoRelaySwitch on", [](auto &M) { return M.AutoRelaySwitch(true); }},
{"IncMainCtrl 1", [](auto &M) { return M.IncMainCtrl(1); }},
{"IncMainCtrl 2", [](auto &M) { return M.IncMainCtrl(2); }},
{"IncScndCtrl 1", [](auto &M) { return M.IncScndCtrl(1); }},
{"IncScndCtrl 2", [](auto &M) { return M.IncScndCtrl(2); }},
{"DecScndCtrl 1", [](auto &M) { return M.DecScndCtrl(1); }},
{"DecScndCtrl 2", [](auto &M) { return M.DecScndCtrl(2); }},
{"DecMainCtrl 1", [](auto &M) { return M.DecMainCtrl(1); }},
{"DecMainCtrl 2", [](auto &M) { return M.DecMainCtrl(2); }},
{"AutoRelaySwitch off", [](auto &M) { return M.AutoRelaySwitch(false); }},
{"CurrentSwitch off", [](auto &M) { return M.CurrentSwitch(false); }},
{"MinCurrentSwitch off", [](auto &M) { return M.MinCurrentSwitch(false); }},
{"MaxCurrentSwitch off", [](auto &M) { return M.MaxCurrentSwitch(false, range_t::unit); }},
{"DirectionBackward", [](auto &M) { return M.DirectionBackward(); }},
{"DirectionBackward again", [](auto &M) { return M.DirectionBackward(); }},
{"DirectionForward", [](auto &M) { return M.DirectionForward(); }},
{"Sandbox on", [](auto &M) { return M.Sandbox(true, range_t::consist); }},
{"SandboxManual on", [](auto &M) { return M.SandboxManual(true, range_t::consist); }},
{"SandboxAuto on", [](auto &M) { return M.SandboxAuto(true, range_t::unit); }},
{"SandboxAuto off", [](auto &M) { return M.SandboxAuto(false, range_t::consist); }},
{"SandboxManual off", [](auto &M) { return M.SandboxManual(false, range_t::consist); }},
{"Sandbox off", [](auto &M) { return M.Sandbox(false, range_t::consist); }},
{"PermitDoors right", [](auto &M) { return M.PermitDoors(side::right, true, range_t::consist); }},
{"PermitDoors left", [](auto &M) { return M.PermitDoors(side::left, true, range_t::unit); }},
{"ChangeDoorPermitPreset +1", [](auto &M) { return M.ChangeDoorPermitPreset(1, range_t::consist); }},
{"ChangeDoorPermitPreset -1", [](auto &M) { return M.ChangeDoorPermitPreset(-1, range_t::consist); }},
{"PermitDoorStep off", [](auto &M) { return M.PermitDoorStep(false, range_t::consist); }},
{"PermitDoorStep on", [](auto &M) { return M.PermitDoorStep(true, range_t::unit); }},
{"LockDoors off", [](auto &M) { return M.LockDoors(false, range_t::consist); }},
{"OperateDoors right open local", [](auto &M) { return M.OperateDoors(side::right, true, range_t::local); }},
{"OperateDoors left open", [](auto &M) { return M.OperateDoors(side::left, true, range_t::consist); }},
{"OperateDoors right close", [](auto &M) { return M.OperateDoors(side::right, false, range_t::unit); }},
{"ChangeDoorControlMode remote", [](auto &M) { return M.ChangeDoorControlMode(true, range_t::consist); }},
{"ChangeDoorControlMode local", [](auto &M) { return M.ChangeDoorControlMode(false, range_t::unit); }},
{"LockDoors on", [](auto &M) { return M.LockDoors(true, range_t::unit); }},
{"signal_departure on", [](auto &M) { return M.signal_departure(true, range_t::consist); }},
{"signal_departure off", [](auto &M) { return M.signal_departure(false, range_t::unit); }},
{"PermitDoors right off", [](auto &M) { return M.PermitDoors(side::right, false, range_t::consist); }},
{"HeatingSwitch off", [](auto &M) { return M.HeatingSwitch(false, range_t::unit); }},
{"MainSwitch off", [](auto &M) { return M.MainSwitch(false, range_t::consist); }},
{"DropAllPantographs", [](auto &M) { return M.DropAllPantographs(true, range_t::consist); }},
{"DropAllPantographs released", [](auto &M) { return M.DropAllPantographs(false, range_t::unit); }},
{"OperatePantographsValve disable", [](auto &M) { return M.OperatePantographsValve(operation_t::disable, range_t::unit); }},
{"OperatePantographValve front disable_on", [](auto &M) { return M.OperatePantographValve(end::front, operation_t::disable_on, range_t::consist); }},
{"OperatePantographValve front disable_off", [](auto &M) { return M.OperatePantographValve(end::front, operation_t::disable_off, range_t::consist); }},
{"OperatePantographValve front none", [](auto &M) { return M.OperatePantographValve(end::front, operation_t::none, range_t::local); }},
{"CompressorSwitch off", [](auto &M) { return M.CompressorSwitch(false, range_t::unit); }},
{"ConverterSwitch off", [](auto &M) { return M.ConverterSwitch(false, range_t::unit); }},
{"WaterCircuitsLinkSwitch off", [](auto &M) { return M.WaterCircuitsLinkSwitch(false, range_t::unit); }},
{"WaterHeaterSwitch off", [](auto &M) { return M.WaterHeaterSwitch(false, range_t::unit); }},
{"WaterHeaterBreakerSwitch off", [](auto &M) { return M.WaterHeaterBreakerSwitch(false, range_t::unit); }},
{"WaterPumpSwitch off", [](auto &M) { return M.WaterPumpSwitch(false, range_t::unit); }},
{"WaterPumpBreakerSwitch off", [](auto &M) { return M.WaterPumpBreakerSwitch(false, range_t::unit); }},
{"FuelPumpSwitch off", [](auto &M) { return M.FuelPumpSwitch(false, range_t::unit); }},
{"OilPumpSwitch off", [](auto &M) { return M.OilPumpSwitch(false, range_t::unit); }},
{"MotorBlowersSwitch front off", [](auto &M) { return M.MotorBlowersSwitch(false, end::front, range_t::unit); }},
{"MotorBlowersSwitch rear off", [](auto &M) { return M.MotorBlowersSwitch(false, end::rear, range_t::consist); }},
{"CompartmentLightsSwitch off", [](auto &M) { return M.CompartmentLightsSwitch(false, range_t::unit); }},
{"BatterySwitch off", [](auto &M) { return M.BatterySwitch(false, range_t::unit); }},
{"CabDeactivisation", [](auto &M) { return M.CabDeactivisation(); }},
};
} // namespace
TEST_CASE("commands: every switch of the cab, sent to the next vehicle") {
for (auto const &path : test::real_vehicles()) {
CAPTURE(path);
test::reseed_random();
vehicle driven{definition::fiz(path), 0.0, "driven", 1};
vehicle next{definition::fiz(path), 0.0, "next"};
driven->Attach(end::rear, end::front, &next.mover(), test::consist::full_coupling, true, false);
auto done{false};
auto channels{state()};
for (auto const &channel : state()) {
channels.emplace_back("next." + channel.name, [&next, value = channel.value](auto const &) { return value(next.mover()); });
}
channels.emplace_back("Done", [&done](auto const &) { return flag(done); });
test::trace trace{"switches/" + path, channels, 1, false};
trace.sample(0.0, driven.mover());
for (std::size_t i = 0; i < actions.size(); ++i) {
trace.mark(actions[i].name);
done = actions[i].work(driven.mover());
trace.sample(static_cast<double>(i + 1), driven.mover());
}
trace.check();
}
}
TEST_CASE("commands: every switch of the cab, worked on a pair standing under the catenary") {
// the same switches with time for the vehicles to act on them, as a crew works them: the pantographs rise,
// the main switch closes, the converter and the compressor start, and the controllers take power
for (auto const &path : test::real_vehicles()) {
CAPTURE(path);
test::reseed_random();
test::consist pair;
pair.add(definition::fiz(path), 1);
pair.add(definition::fiz(path));
auto done{false};
auto channels{state()};
channels.insert(channels.end(), {
{"PantFrontActive", [](auto const &M) { return flag(M.Pantographs[end::front].is_active); }},
{"PantRearActive", [](auto const &M) { return flag(M.Pantographs[end::rear].is_active); }},
{"PantPress", [](auto const &M) { return M.PantPress; }},
{"GroundRelay", [](auto const &M) { return flag(M.GroundRelay); }},
{"ConverterFlag", [](auto const &M) { return flag(M.ConverterFlag); }},
{"CompressorFlag", [](auto const &M) { return flag(M.CompressorFlag); }},
{"Compressor", [](auto const &M) { return M.Compressor; }},
{"MainCtrlActualPos", [](auto const &M) { return M.MainCtrlActualPos; }},
{"Itot", [](auto const &M) { return M.Itot; }},
{"FuseFlag", [](auto const &M) { return flag(M.FuseFlag); }},
{"EngineRotation", [](auto const &M) { return M.enrot; }},
});
auto const own{channels.size()};
for (std::size_t i = 0; i < own; ++i) {
channels.emplace_back("next." + channels[i].name, [&pair, value = channels[i].value](auto const &) { return value(pair[1].mover()); });
}
channels.emplace_back("Done", [&done](auto const &) { return flag(done); });
test::trace trace{"switches_live/" + path, channels, 1, false};
trace.sample(0.0, pair[0].mover());
test::track const catenary{.voltage = test::catenary_3kv};
for (std::size_t i = 0; i < actions.size(); ++i) {
trace.mark(actions[i].name);
done = actions[i].work(pair[0].mover());
pair.run(2.0, catenary);
trace.sample(static_cast<double>(i + 1), pair[0].mover());
}
trace.check();
}
}
namespace {
// three locomotives coupled with the given coupling, the middle one facing backwards, as a train made up of
// whatever came to hand: the commands of the front one go to the rear coupler of the middle one and from its
// front coupler to the front one of the last, which faces backwards from the middle one, so forwards
struct train {
vehicle head{definition::fiz("4e_v1/4e"), 0.0, "front", 1};
vehicle middle{definition::fiz("4e_v1/4e"), 0.0, "middle"};
vehicle last{definition::fiz("4e_v1/4e"), 0.0, "last"};
explicit train(int Coupling) {
head->Attach(end::rear, end::rear, &middle.mover(), Coupling, true, false);
middle->Attach(end::front, end::front, &last.mover(), Coupling, true, false);
}
};
} // namespace
TEST_CASE("commands: travel along the train, turned round for a vehicle facing backwards") {
train consist{test::consist::full_coupling};
// the driver takes the front cab, which the others follow
REQUIRE(consist.head->CabActivisation());
CHECK(consist.head->CabActive == 1);
CHECK(consist.middle->CabActive == -1);
CHECK(consist.last->CabActive == 1);
// the command is done in each vehicle and cleared
CHECK(consist.middle->CommandIn.Command.empty());
CHECK(consist.last->CommandIn.Command.empty());
SUBCASE("door permits: the right side of the sender is the left one of a vehicle facing backwards") {
consist.head->PermitDoors(side::right, true, range_t::consist);
CHECK(consist.head->Doors.instances[side::right].open_permit);
CHECK(consist.middle->Doors.instances[side::left].open_permit);
CHECK_FALSE(consist.middle->Doors.instances[side::right].open_permit);
CHECK(consist.last->Doors.instances[side::right].open_permit);
// and revoked the same way
consist.head->PermitDoors(side::right, false, range_t::consist);
CHECK_FALSE(consist.middle->Doors.instances[side::left].open_permit);
CHECK_FALSE(consist.last->Doors.instances[side::right].open_permit);
}
SUBCASE("a pantograph valve: the vehicles past one coupled by its control cable take the other pantograph") {
consist.head->OperatePantographValve(end::front, operation_t::enable, range_t::consist);
CHECK(consist.head->Pantographs[end::front].valve.is_enabled);
CHECK(consist.middle->Pantographs[end::front].valve.is_enabled);
CHECK(consist.last->Pantographs[end::rear].valve.is_enabled);
}
SUBCASE("sent backwards, it goes no further than the sender: nothing is coupled at its front") {
consist.head->RunCommand("DoorLock", 0, -1, coupling::control);
CHECK_FALSE(consist.head->Doors.lock_enabled);
CHECK(consist.middle->Doors.lock_enabled);
}
SUBCASE("the same command again isn't taken by the next vehicle a second time") {
CHECK(consist.middle->SetInternalCommand("DoorStep", 0, 1, coupling::control));
CHECK_FALSE(consist.middle->SetInternalCommand("DoorStep", 0, 1, coupling::control));
CHECK_FALSE(consist.head->RunInternalCommand()); // nothing to run
}
}
TEST_CASE("commands: don't pass a coupling without the control cable") {
train consist{coupling::coupler | coupling::brakehose};
consist.head->RunCommand("DoorLock", 0, 1, coupling::control);
CHECK_FALSE(consist.head->Doors.lock_enabled);
CHECK(consist.middle->Doors.lock_enabled);
// a command of another coupling goes through it
consist.head->RunCommand("DoorStep", 0, 1, coupling::brakehose);
CHECK_FALSE(consist.middle->Doors.step_enabled);
}
TEST_CASE("commands: a wagon loaded and unloaded by commands, which repeat until done") {
vehicle wagon{definition::fiz("3wl_v2/3wl")};
REQUIRE(wagon->MaxLoad > 0);
test::trace trace{"commands/loading", state(), 100};
wagon.record(trace);
// the game gives the commands of loading through PutCommand, with the place of the platform or the ramp
SUBCASE("loading 12 tonnes of coal") {
trace.mark("load");
wagon->PutCommand("Load=Coal", 12, 0, wagon->Loc);
wagon.run(30.0);
CHECK(wagon->LoadAmount == doctest::Approx(12.0).epsilon(0.1));
CHECK(wagon->LoadType.name == "coal");
CHECK(wagon->CommandIn.Command.empty());
trace.mark("unload");
wagon->PutCommand("UnLoad=Coal", 20, 0, wagon->Loc);
wagon.run(200.0);
CHECK(wagon->LoadAmount == doctest::Approx(0.0));
CHECK(wagon->LoadType.name.empty());
trace.check();
}
SUBCASE("a load the wagon doesn't take stays a command, not done") {
wagon->PutCommand("Load=passengers", 12, 0, wagon->Loc);
wagon.run(10.0);
CHECK(wagon->LoadAmount == doctest::Approx(0.0));
CHECK_FALSE(wagon->CommandIn.Command.empty());
}
SUBCASE("away from the place of the command nothing is loaded") {
wagon->PutCommand("Load=Coal", 12, 5.0, TLocation{100.0, 0.0, 0.0});
wagon.run(10.0);
CHECK(wagon->LoadAmount == doctest::Approx(0.0));
CHECK(wagon->LastLoadChangeTime == doctest::Approx(0.0));
}
SUBCASE("unloading an empty wagon isn't done either") {
wagon->PutCommand("UnLoad=Coal", 12, 0, wagon->Loc);
wagon.run(10.0);
CHECK_FALSE(wagon->CommandIn.Command.empty());
}
}

135
tests/unit/test_consist.cpp Normal file
View File

@@ -0,0 +1,135 @@
#include "stdafx.h"
#include "doctest.h"
#include "consist.h"
#include "drive.h"
#include "golden.h"
#include "McZapkie/hamulce.h"
using test::consist;
using test::definition;
// characterisation of trains of coupled vehicles: the real electric units driven whole, a locomotive with
// wagons, the air, the control and the coupler forces between the vehicles, in golden traces
// (data/golden/consist/), each vehicle of the train in its own
namespace {
struct unit {
char const *name;
std::vector<char const *> cars; // from the front, the first one driven
};
std::vector<unit> const units{
{"en57", {"en57_v1/5bgaiii_led_spot", "en57_v1/6ba_840", "en57_v1/5bgb"}},
{"ed72", {"ed72_v1/5bs-ra", "ed72_v1/6bs-sa", "ed72_v1/6bs-sb", "ed72_v1/5bs-rb"}},
{"ew58", {"ew58_v1/ew58-sa", "ew58_v1/ew58-d", "ew58_v1/ew58-sb"}},
{"elf", {"elf_v1/21we-a", "elf_v1/21we-b", "elf_v1/21we-c"}},
{"impuls", {"impuls_v1/31we-a_pr", "impuls_v1/31we-b_pr", "impuls_v1/31we-c_pr", "impuls_v1/31we-d_pr"}},
};
std::vector<test::channel> car_channels() {
using namespace test::channels;
return motion() + traction() + air() + brakes();
}
// a trace for each vehicle of the train, recording it
std::vector<std::unique_ptr<test::trace>> record(consist &Train, std::string const &Name) {
std::vector<std::unique_ptr<test::trace>> traces;
for (std::size_t i = 0; i < Train.size(); ++i) {
traces.push_back(std::make_unique<test::trace>(std::format("consist/{}/{}", Name, i), car_channels(), 100));
Train[i].record(*traces.back());
}
return traces;
}
// the events go to every trace
struct all_traces {
std::vector<std::unique_ptr<test::trace>> &traces;
void mark(std::string const &Event) const {
for (auto const &trace : traces) {
trace->mark(Event);
}
}
};
} // namespace
TEST_CASE("consist: every real electric unit driven whole from its front cab") {
for (auto const &unit : units) {
CAPTURE(unit.name);
test::reseed_random();
consist train;
for (auto const *car : unit.cars) {
train.add(definition::fiz(car), train.size() == 0 ? 1 : 0);
}
auto traces{record(train, unit.name)};
// the drive cycle marks the events in the trace it gets; the first car's carries them
test::drive_cycle(test::driving(train), test::track{0.15, 0.0, test::catenary_3kv}, *traces.front());
// every unit runs off and is braked to a stop, but the EN57, which doesn't get ready (COVERAGE.md)
auto const &leading{train[0].mover()};
CHECK((leading.DistCounter > 0.2 /* km */) != (std::string{unit.name} == "en57"));
CHECK(leading.Vel < 0.5);
for (auto const &trace : traces) {
trace->check();
}
}
}
TEST_CASE("consist: a locomotive with wagons, driven, braked by its handle and pulled apart by the couplers") {
test::reseed_random();
consist train;
train.add(definition::fiz("4e_v1/4e"), 1);
for (auto const *wagon : {"3wl_v2/3wl", "11xa_v2/93a", "bdhpumn_v3/bdhpumn", "z1_v2/152a"}) {
train.add(definition::fiz(wagon));
}
auto traces{record(train, "eu07 with wagons")};
test::drive_cycle(test::driving(train), test::track{0.15, 0.0, test::catenary_3kv}, *traces.front());
for (auto const &trace : traces) {
trace->check();
}
// the couplers kept the train together
for (std::size_t i = 0; i + 1 < train.size(); ++i) {
CAPTURE(i);
CHECK(std::abs(train.gap(i)) < 0.5);
}
}
namespace {
// what the cab's ammeters show (Train.cpp, the driver's panel) and the brake force the AI expects of the
// vehicle (Driver.cpp), read of the driven vehicle
std::vector<test::channel> gauges() {
std::vector<test::channel> result;
for (int ammeter = 0; ammeter < 4; ++ammeter) {
result.emplace_back(std::format("ShowCurrent{}", ammeter), [ammeter](auto const &M) { return M.ShowCurrent(ammeter); });
}
result.emplace_back("BrakeForceR0.25", [](auto const &M) { return M.BrakeForceR(0.25, M.Vel); });
result.emplace_back("BrakeForceR1", [](auto const &M) { return M.BrakeForceR(1.0, M.Vel); });
result.emplace_back("BrakeForceR1at20", [](auto const &M) { return M.BrakeForceR(1.0, 20.0); });
using test::channels::operator+;
return result + test::channels::traction();
}
} // namespace
TEST_CASE("consist: the gauges of units and locomotives in multiple, driven") {
auto trains{units};
trains.push_back({"eu07 pair", {"4e_v1/4e", "4e_v1/4e"}});
trains.push_back({"et22 pair", {"et22_v3/201e-w", "et22_v3/201e-rn"}});
trains.push_back({"ep09 pair", {"ep09_v2/104e", "ep09_v2/104e"}});
trains.push_back({"sn61 three", {"sn61_v2/sn61", "sn61_v2/sn61", "sn61_v2/sn61"}});
trains.push_back({"su45 pair", {"su45_v2/301d", "su45_v2/301d"}});
for (auto const &train : trains) {
CAPTURE(train.name);
test::reseed_random();
consist vehicles;
for (auto const *car : train.cars) {
vehicles.add(definition::fiz(car), vehicles.size() == 0 ? 1 : 0);
}
test::trace trace{std::string{"gauges/"} + train.name, gauges(), 100};
vehicles[0].record(trace);
test::drive_cycle(test::driving(vehicles), test::track{0.15, 0.0, test::catenary_3kv}, trace);
trace.check();
}
}

View File

@@ -0,0 +1,632 @@
#include "stdafx.h"
#include "doctest.h"
#include "scenario.h"
#include "drive.h"
#include "McZapkie/hamulce.h"
#include "utilities/utilities.h"
// characterisation of the controls a driver and a crew use besides driving and braking: doors, direction and
// cab change, sanding against slip, the emergency brake, releasers, the ep and the dynamic brake, heating,
// loading, collisions and the frame of several physics iterations, each in a scenario on real vehicles,
// recorded in golden traces (data/golden/controls/)
using test::scenario;
using namespace test::channels;
namespace {
void notch_up(scenario &Scenario, int Positions, double Seconds) {
for (int i = 0; i < Positions; ++i) {
Scenario.driver().controlling.IncMainCtrl(1);
Scenario.wait(Seconds);
}
}
void notch_down(scenario &Scenario) {
while (Scenario.driver().controlling.DecMainCtrl(1)) {
Scenario.wait(0.5);
}
Scenario.driver().controlling.DecScndCtrl(2);
}
void brake_to_stop(scenario &Scenario, double Seconds = 60.0) {
Scenario.act("brake to stop");
Scenario.cab().BrakeLevelSet(Scenario.cab().Handle->GetPos(bh_FB));
Scenario.wait(Seconds);
Scenario.cab().BrakeLevelSet(Scenario.cab().Handle->GetPos(bh_RP));
Scenario.wait(20.0);
}
// the pipe charged again after an emergency stop: the releaser held with the handle at running, then let go
void recharge(scenario &Scenario) {
auto &cab{Scenario.cab()};
Scenario.act("releaser held, the pipe charged");
cab.BrakeLevelSet(cab.Handle->GetPos(bh_RP));
cab.BrakeReleaser(1);
Scenario.wait(30.0);
cab.BrakeReleaser(0);
Scenario.wait(30.0);
}
bool stopped(scenario &Scenario) {
for (std::size_t i = 0; i < Scenario.train().size(); ++i) {
if (Scenario.vehicle(i).Vel > 0.1) {
return false;
}
}
return true;
}
// the brake cylinders of every vehicle of the train under the pressure given
bool released(scenario &Scenario, double Pressure = 0.1) {
for (std::size_t i = 0; i < Scenario.train().size(); ++i) {
if (Scenario.vehicle(i).BrakePress > Pressure) {
return false;
}
}
return true;
}
bool open(TMoverParameters const &Vehicle, int Side) {
return Vehicle.Doors.instances[Side].is_open;
}
// the leaf shut, whatever the step does (closed, is_closed, wants the step folded too)
bool shut(TMoverParameters const &Vehicle, int Side) {
return Vehicle.Doors.instances[Side].position <= 0.f;
}
bool closed(TMoverParameters const &Vehicle, int Side) {
return Vehicle.Doors.instances[Side].is_closed;
}
} // namespace
TEST_CASE("controls: the doors of an electric unit, opened, locked, permitted and closed by driving off") {
for (auto const &[name, cars] : std::vector<std::pair<std::string, std::vector<char const *>>>{
{"elf", {"elf_v1/21we-a", "elf_v1/21we-b", "elf_v1/21we-c"}},
{"en57", {"en57_v1/5bgaiii_led_spot", "en57_v1/6ba_840", "en57_v1/5bgb"}},
{"impuls", {"impuls_v1/31we-a_pr", "impuls_v1/31we-b_pr", "impuls_v1/31we-c_pr", "impuls_v1/31we-d_pr"}}}) {
CAPTURE(name);
scenario scenario{"controls/doors/" + name, motion() + doors()};
for (auto const *car : cars) {
scenario.add(car, scenario.train().size() == 0 ? 1 : 0);
}
// the EN57 doesn't get ready (COVERAGE.md), its doors work all the same
auto const ready{scenario.start_up()};
CHECK(ready == (name != "en57"));
auto &cab{scenario.cab()};
auto const &last{scenario.vehicle(scenario.train().size() - 1)};
scenario.act("permit left");
cab.PermitDoors(side::left, true);
scenario.wait(2.0);
scenario.act("open left");
cab.OperateDoors(side::left, true);
scenario.wait(10.0);
CHECK(open(cab, side::left));
CHECK(open(last, side::left));
CHECK(closed(cab, side::right));
scenario.act("close left");
cab.OperateDoors(side::left, false);
scenario.wait(10.0);
CHECK(shut(cab, side::left));
CHECK(shut(last, side::left));
scenario.act("open right without permit");
cab.OperateDoors(side::right, true);
scenario.wait(5.0);
// the EN57 needs no permit
CHECK(shut(cab, side::right) == cab.Doors.permit_needed);
scenario.act("permit right, open");
cab.PermitDoors(side::right, true);
cab.OperateDoors(side::right, true);
scenario.wait(10.0);
CHECK(open(cab, side::right));
CHECK(open(last, side::right));
scenario.act("lock");
cab.LockDoors(true);
scenario.wait(5.0);
scenario.act("permit preset, steps off, remote control");
cab.ChangeDoorPermitPreset(1);
cab.PermitDoorStep(false);
cab.ChangeDoorControlMode(true);
scenario.wait(5.0);
cab.ChangeDoorPermitPreset(-1);
cab.PermitDoorStep(true);
cab.ChangeDoorControlMode(false);
scenario.act("close all, unlock");
cab.OperateDoors(side::right, false);
cab.OperateDoors(side::left, false);
cab.LockDoors(false);
scenario.wait(10.0);
CHECK(shut(cab, side::left));
CHECK(shut(cab, side::right));
scenario.act("open both and drive off");
cab.PermitDoors(side::left, true);
cab.PermitDoors(side::right, true);
cab.OperateDoors(side::left, true);
cab.OperateDoors(side::right, true);
scenario.wait(5.0);
CHECK_FALSE(shut(cab, side::left));
CHECK_FALSE(shut(cab, side::right));
scenario.get_going();
notch_up(scenario, 4, 2.0);
scenario.wait(20.0);
// the doors of a train ready to run close as the driver starts off. no CHECK of the speed: the Elf and
// the Impuls keep their steps out under the permit, which keeps them from moving (COVERAGE.md)
CHECK(shut(cab, side::left) == ready);
CHECK(shut(cab, side::right) == ready);
CHECK(shut(last, side::left) == ready);
CHECK(shut(last, side::right) == ready);
notch_down(scenario);
brake_to_stop(scenario);
scenario.check();
}
}
TEST_CASE("controls: the doors of wagons, worked from the locomotive") {
scenario scenario{"controls/doors/eu07 with wagons", motion() + doors()};
scenario.add("4e_v1/4e", 1).add("bdhpumn_v3/bdhpumn").add("11xa_v2/93a");
scenario.start_up();
auto &cab{scenario.cab()};
auto &coach{scenario.vehicle(1)};
auto const &freight{scenario.vehicle(2)};
// the locomotive works the coach's doors for the consist and for the unit, not with its local controls; the
// freight wagon's doors don't move from the locomotive
for (auto const range : {range_t::consist, range_t::unit, range_t::local}) {
CAPTURE(static_cast<int>(range));
scenario.act(std::format("open right {}", static_cast<int>(range)));
cab.PermitDoors(side::right, true, range);
cab.OperateDoors(side::right, true, range);
scenario.wait(10.0);
CHECK(open(coach, side::right) == (range != range_t::local));
CHECK(shut(coach, side::left));
CHECK(shut(freight, side::right));
scenario.act("close right");
cab.OperateDoors(side::right, false, range);
scenario.wait(10.0);
CHECK(shut(coach, side::right));
}
scenario.act("wagon opens its own");
coach.OperateDoors(side::left, true, range_t::local);
// a second at a time, the same run as one wait. the coach's doors close by themselves 5 s after they
// opened (DoorStayOpen)
scenario.wait(1.0);
CHECK_FALSE(shut(coach, side::left));
scenario.wait(9.0);
CHECK(shut(coach, side::left));
coach.OperateDoors(side::left, false, range_t::local);
scenario.wait(10.0);
CHECK(shut(coach, side::left));
scenario.check();
}
TEST_CASE("controls: reversing and changing the cab of a locomotive") {
scenario scenario{"controls/reverse/eu07", motion() + traction()};
scenario.add("4e_v1/4e", 1);
scenario.start_up();
scenario.get_going();
auto &cab{scenario.cab()};
// the speed along the vehicle, its front ahead
notch_up(scenario, 10, 1.5);
scenario.wait(10.0);
CHECK(cab.V > 3.0);
notch_down(scenario);
brake_to_stop(scenario);
CHECK(stopped(scenario));
scenario.act("backward");
cab.DirectionBackward();
cab.DirectionBackward();
scenario.wait(2.0);
CHECK(cab.DirActive < 0);
notch_up(scenario, 10, 1.5);
scenario.wait(10.0);
CHECK(cab.V < -3.0);
notch_down(scenario);
brake_to_stop(scenario);
CHECK(stopped(scenario));
scenario.act("neutral, change cab");
cab.DirectionForward();
scenario.wait(1.0);
cab.CabDeactivisation();
// through the engine room (cab 0) to the rear cab, as the player walks there
cab.ChangeCab(-1);
cab.ChangeCab(-1);
cab.CabActivisation();
scenario.wait(5.0);
CHECK(cab.CabActive < 0);
scenario.act("drive from the rear cab");
scenario.get_going();
notch_up(scenario, 10, 1.5);
scenario.wait(10.0);
// forward from the rear cab is the vehicle's backward
CHECK(cab.V < -3.0);
notch_down(scenario);
brake_to_stop(scenario);
CHECK(stopped(scenario));
scenario.check();
}
TEST_CASE("controls: wheel slip on a slippery track, sand and the anti-slip brake") {
for (auto const *path : {"4e_v1/4e", "sm42_v1/6d1", "e186_v2/eu47"}) {
CAPTURE(path);
scenario scenario{std::string{"controls/slip/"} + path, motion() + traction() + brakes(), test::track{0.04, 0.0, test::catenary_3kv}};
scenario.add(path, 1);
scenario.start_up();
scenario.get_going();
auto &cab{scenario.cab()};
// a notch a second: the controller put to full power at once never gets the line contactors closed
scenario.act("full power");
scenario.full_power(0.0);
auto slipped{cab.SlippingWheels};
for (int i = 0; i < 10; ++i) {
scenario.wait(1.0);
slipped = slipped || cab.SlippingWheels;
}
// the electric locomotives slip at full power, the SM42, of less power for its weight, doesn't
CHECK(slipped == (std::string{path} != "sm42_v1/6d1"));
scenario.act("sand");
cab.Sandbox(true);
scenario.wait(10.0);
cab.Sandbox(false);
scenario.act("manual sand");
cab.SandboxManual(true);
scenario.wait(5.0);
cab.SandboxManual(false);
scenario.act("anti-slip brake");
cab.AntiSlippingButton();
cab.AntiSlippingBrake();
scenario.wait(10.0);
CHECK(cab.Vel > 5.0);
notch_down(scenario);
brake_to_stop(scenario);
CHECK(stopped(scenario));
scenario.check();
}
}
TEST_CASE("controls: emergency stops by the alarm chain and the radio-stop") {
scenario scenario{"controls/emergency/eu07 with wagons", motion() + brakes()};
scenario.add("4e_v1/4e", 1).add("bdhpumn_v3/bdhpumn").add("3wl_v2/3wl");
scenario.start_up();
scenario.get_going();
notch_up(scenario, 20, 1.0);
scenario.wait(20.0);
notch_down(scenario);
auto &cab{scenario.cab()};
CHECK(cab.Vel > 30.0);
scenario.act("alarm chain in the wagon");
scenario.vehicle(1).AlarmChainSwitch(true);
scenario.wait(40.0);
// the pipe emptied, the whole train braked to a stop
CHECK(stopped(scenario));
CHECK(cab.PipePress < 0.5);
CHECK(scenario.vehicle(2).BrakePress > 1.0);
scenario.act("alarm chain reset");
scenario.vehicle(1).AlarmChainSwitch(false);
scenario.wait(10.0);
// the FV4a handle stays cut off the emptied pipe of an LSt locomotive (lock_old in UpdatePipePressure)
// until the releaser is held
CHECK(cab.PipePress < 0.5);
recharge(scenario);
CHECK(cab.PipePress > 4.5);
scenario.get_going();
notch_up(scenario, 20, 1.0);
scenario.wait(20.0);
notch_down(scenario);
CHECK(cab.Vel > 30.0);
scenario.act("radio-stop");
cab.RadiostopSwitch(true);
scenario.wait(40.0);
CHECK(stopped(scenario));
CHECK(cab.PipePress < 0.5);
scenario.act("radio-stop reset");
cab.RadiostopSwitch(false);
scenario.wait(10.0);
recharge(scenario);
CHECK(cab.PipePress > 4.5);
scenario.check();
}
TEST_CASE("controls: releasers and the universal brake buttons") {
scenario scenario{"controls/releaser/eu07 with wagons", brakes() + air()};
scenario.add("4e_v1/4e", 1).add("bdhpumn_v3/bdhpumn").add("3wl_v2/3wl");
scenario.start_up();
scenario.get_going();
auto &cab{scenario.cab()};
auto &wagon{scenario.vehicle(2)};
scenario.act("service brake");
cab.BrakeLevelSet(cab.Handle->GetPos(bh_FB));
scenario.wait(20.0);
CHECK(cab.BrakePress > 1.0);
CHECK(wagon.BrakePress > 1.0);
cab.BrakeLevelSet(cab.Handle->GetPos(bh_NP));
auto &coach{scenario.vehicle(1)};
// the releaser of the cab is passed along the whole train, a wagon's works on the wagon alone
auto const coachbraked{coach.BrakePress};
auto wagonbraked{wagon.BrakePress};
scenario.act("releaser of the locomotive");
cab.BrakeReleaser(1);
scenario.wait(10.0);
cab.BrakeReleaser(0);
CHECK(cab.BrakePress < 0.5);
CHECK(coach.BrakePress < coachbraked - 1.0);
CHECK(wagon.BrakePress < wagonbraked - 0.5);
CHECK(cab.PipePress < 4.0); // the pipe stays where the service application left it
wagonbraked = wagon.BrakePress;
scenario.act("releaser of the wagon");
wagon.BrakeReleaser(1);
scenario.wait(10.0);
wagon.BrakeReleaser(0);
CHECK(wagon.BrakePress < wagonbraked - 0.5);
for (int button = 0; button < 3; ++button) {
scenario.act(std::format("universal button {}", button));
cab.UniversalBrakeButton(button, 1);
scenario.wait(5.0);
cab.UniversalBrakeButton(button, 0);
scenario.wait(5.0);
}
scenario.act("running");
cab.BrakeLevelSet(cab.Handle->GetPos(bh_RP));
scenario.wait(60.0);
CHECK(cab.PipePress > 4.8);
CHECK(released(scenario));
scenario.check();
}
TEST_CASE("controls: the ep brake of electric units") {
for (auto const &[name, cars] : std::vector<std::pair<std::string, std::vector<char const *>>>{
{"ed72", {"ed72_v1/5bs-ra", "ed72_v1/6bs-sa", "ed72_v1/6bs-sb", "ed72_v1/5bs-rb"}},
{"ew58", {"ew58_v1/ew58-sa", "ew58_v1/ew58-d", "ew58_v1/ew58-sb"}}}) {
CAPTURE(name);
scenario scenario{"controls/ep brake/" + name, motion() + brakes() + handles()};
for (auto const *car : cars) {
scenario.add(car, scenario.train().size() == 0 ? 1 : 0);
}
scenario.start_up();
scenario.get_going();
auto &cab{scenario.cab()};
scenario.act("ep on");
cab.SwitchEPBrake(1);
scenario.wait(5.0);
auto const &last{scenario.vehicle(scenario.train().size() - 1)};
auto const positions{std::vector{bh_EPB, bh_EPN, bh_EPR, bh_EPB, bh_RP}};
for (std::size_t i = 0; i < positions.size(); ++i) {
CAPTURE(positions[i]);
scenario.act(std::format("handle {}", positions[i]));
cab.BrakeLevelSet(cab.Handle->GetPos(positions[i]));
scenario.wait(15.0);
if (i == 2) {
CHECK(released(scenario));
}
if (i == 3) {
// the ep brake works the cylinders of every car by wire, the pipe stays charged (by the first
// application the EW58's pipe is still charging after the start-up)
CHECK(cab.PipePress > 4.8);
CHECK(cab.BrakePress > 1.0);
CHECK(last.BrakePress > 1.0);
}
}
// the EW58 releases at the running position, the ED72 holds the ep application there, and with the ep
// brake switched off as well: only the ep release position lets it off
auto const ed72{name == "ed72"};
CHECK(released(scenario) != ed72);
scenario.act("ep off");
cab.SwitchEPBrake(0);
scenario.wait(30.0);
CHECK(released(scenario) != ed72);
scenario.check();
}
}
TEST_CASE("controls: the electrodynamic brake of locomotives having one") {
for (auto const *path : {"ep09_v2/104e", "e186_v2/eu47", "e6act_v2/e6acta", "et22_v3/201e-rn"}) {
CAPTURE(path);
scenario scenario{std::string{"controls/dynamic brake/"} + path, motion() + traction() + brakes()};
scenario.add(path, 1).add("bdhpumn_v3/bdhpumn").add("bdhpumn_v3/bdhpumn");
scenario.start_up();
scenario.get_going();
notch_up(scenario, 60, 1.0);
scenario.wait(30.0);
notch_down(scenario);
auto &cab{scenario.cab()};
CHECK(cab.Vel > 40.0);
auto const speed{cab.Vel};
scenario.act("dynamic brake on");
cab.DynamicBrakeSwitch(true);
scenario.wait(5.0);
scenario.act("brake");
cab.BrakeLevelSet(cab.Handle->GetPos(bh_FB));
scenario.wait(40.0);
CHECK(cab.Vel < speed);
scenario.act("dynamic brake off");
cab.DynamicBrakeSwitch(false);
brake_to_stop(scenario, 30.0);
CHECK(stopped(scenario));
scenario.check();
}
}
TEST_CASE("controls: train heating from the locomotive") {
scenario scenario{"controls/heating/eu07 with wagons", traction() + air()};
scenario.add("4e_v1/4e", 1).add("bdhpumn_v3/bdhpumn").add("bdhpumn_v3/bdhpumn");
scenario.start_up();
auto &cab{scenario.cab()};
scenario.act("heating on");
cab.HeatingSwitch(true);
scenario.wait(30.0);
CHECK(cab.Heating);
CHECK(scenario.vehicle(2).Heating);
scenario.act("heating off");
cab.HeatingSwitch(false);
scenario.wait(10.0);
CHECK_FALSE(cab.Heating);
CHECK_FALSE(scenario.vehicle(2).Heating);
scenario.check();
}
TEST_CASE("controls: a freight wagon loaded and braked") {
auto emptymass{0.0};
for (auto const *load : {"", "coal", "ruda", "unknown"}) {
CAPTURE(load);
scenario scenario{std::string{"controls/load/3wl "} + (*load ? load : "empty"), motion() + brakes()};
scenario.add("4e_v1/4e", 1).add("3wl_v2/3wl");
auto &wagon{scenario.vehicle(1)};
auto const known{std::string{load} == "coal" || std::string{load} == "ruda"};
CHECK(wagon.AssignLoad(load, 30.f) == (known || std::string{load}.empty()));
wagon.ComputeMass();
if (*load == '\0') {
emptymass = wagon.TotalMass;
}
else {
// 30 t of a load the wagon takes, none of one it doesn't
CHECK(wagon.TotalMass == doctest::Approx(emptymass + (known ? 30000.0 : 0.0)).epsilon(0.01));
}
scenario.start_up();
scenario.get_going();
notch_up(scenario, 20, 1.0);
scenario.wait(20.0);
notch_down(scenario);
CHECK(scenario.cab().Vel > 20.0);
brake_to_stop(scenario);
CHECK(stopped(scenario));
scenario.check();
}
}
TEST_CASE("controls: a locomotive running into a standing wagon") {
for (auto const speed : {5.0, 15.0, 40.0}) {
CAPTURE(speed);
scenario scenario{std::format("controls/collision/{} kmh", static_cast<int>(speed)), motion()};
// the wagon stands in front, the locomotive comes from behind it
scenario.add("bdhpumn_v3/bdhpumn").add("4e_v1/4e", 0, coupling::faux, speed, 30.0);
auto const &wagon{scenario.vehicle(0)};
scenario.wait(30.0);
// slow, the locomotive pushes the wagon off, the buffers only touching, and both roll to a stop; fast,
// the wagon derails where it stands
auto const derailed{speed > 30.0};
CHECK(TestFlag(wagon.DamageFlag, dtrain_out) == derailed);
CHECK((std::abs(wagon.DistCounter) > 0.0001) != derailed);
CHECK(stopped(scenario));
scenario.check();
}
}
TEST_CASE("controls: a train run with five physics steps in a frame") {
scenario scenario{"controls/full physics/eu07 with wagons", motion() + traction() + brakes(),
test::track{0.15, 0.0, test::catenary_3kv, 0, 5}};
scenario.add("4e_v1/4e", 1).add("bdhpumn_v3/bdhpumn").add("3wl_v2/3wl");
scenario.start_up();
scenario.get_going();
notch_up(scenario, 25, 1.0);
scenario.wait(30.0);
notch_down(scenario);
// no CHECKs of the run: with five steps a frame the motors take no current and the train rolls at a few
// km/h, which is a finding (COVERAGE.md), not the behaviour to keep
brake_to_stop(scenario);
scenario.check();
}
TEST_CASE("controls: the automatic electrodynamic brake of the EP09, in service braking held to a stop") {
// held for longer, a service application of the train brake makes the EP09 brake electrodynamically: the
// pressure its distributor asks for (GetEDBCP) brings the motors in as generators, the air of its own
// cylinders is let out, and given back as the train slows down and the motors' brake force fades
struct application {
char const *name;
double steps; // past the first braking position of the handle
};
for (auto const &[name, steps] : {application{"light", 0.0}, application{"medium", 1.0}, application{"high", 2.0}, application{"full service", 3.0}}) {
CAPTURE(name);
scenario scenario{std::string{"controls/dynamic brake held/ep09 "} + name, motion() + traction() + brakes()};
scenario.add("ep09_v2/104e", 1, test::consist::full_coupling, 120.0);
scenario.add("bdhpumn_v3/bdhpumn", 0, test::consist::full_coupling, 120.0);
scenario.add("bdhpumn_v3/bdhpumn", 0, test::consist::full_coupling, 120.0);
REQUIRE(scenario.start_up());
auto &ep09{scenario.cab()};
REQUIRE(ep09.Power110vIsAvailable);
scenario.act(std::string{"brake "} + name);
ep09.BrakeLevelSet(ep09.Handle->GetPos(bh_MB) + steps);
scenario.wait(10.0);
CHECK(ep09.DynamicBrakeFlag);
CHECK(ep09.Im < -100.0);
CHECK(ep09.BrakePress < 0.25 * ep09.Hamulec->GetEDBCP());
scenario.wait(110.0);
// stopped, held by the air alone. the flag of the electrodynamic brake stays on while the cylinders hold
// less than 2.1 bar, though the motors give nothing; a stronger application drops it before the stop
CHECK(ep09.Vel == doctest::Approx(0.0));
CHECK(ep09.Im == doctest::Approx(0.0));
CHECK(ep09.DynamicBrakeFlag == (ep09.BrakePress <= 2.1));
CHECK(ep09.BrakePress == doctest::Approx(ep09.Hamulec->GetEDBCP()).epsilon(0.05));
scenario.act("release");
ep09.BrakeLevelSet(ep09.Handle->GetPos(bh_RP));
scenario.wait(30.0);
scenario.check();
}
}
TEST_CASE("controls: the automatic electrodynamic brake under ep and service braking, from speed") {
// ep, light and full service braking of trains running at speed, whose locomotive or unit has the automatic
// electrodynamic brake
struct train {
char const *name;
std::vector<char const *> cars; // the first one driven
double speed; // km/h at the start
bool epelectric; // whether the ep brake alone brings the motors in
};
// the EW58's motors brake by the stages of the ep brake (TUHEX), with the 110 V of the converter in its
// middle car; the E6ACT's by its distributor, from a deeper service application
for (auto const &[name, cars, speed, epelectric] : {
train{"e6act with wagons", {"e6act_v2/e6acta", "bdhpumn_v3/bdhpumn", "bdhpumn_v3/bdhpumn"}, 100.0, false},
train{"ew58", {"ew58_v1/ew58-sa", "ew58_v1/ew58-d", "ew58_v1/ew58-sb"}, 90.0, true},
}) {
CAPTURE(name);
scenario scenario{std::string{"controls/dynamic brake from speed/"} + name, motion() + traction() + brakes()};
for (auto const *car : cars) {
scenario.add(car, car == cars.front() ? 1 : 0, test::consist::full_coupling, speed);
}
scenario.start_up();
auto &cab{scenario.cab()};
auto const brake{[&scenario, &cab](char const *Event, double Position, double Seconds) {
scenario.act(Event);
cab.BrakeLevelSet(Position);
scenario.wait(Seconds);
}};
brake("ep brake", cab.Handle->GetPos(bh_EPB), 15.0);
CHECK(cab.DynamicBrakeFlag == epelectric);
CHECK((cab.Im < -100.0) == epelectric);
brake("ep hold", cab.Handle->GetPos(bh_EPN), 5.0);
brake("first step", cab.Handle->GetPos(bh_MB), 20.0);
brake("deeper", cab.Handle->GetPos(bh_MB) + 1.0, 15.0);
// the pipe alone doesn't bring the EW58's motors in
CHECK((cab.Im < -50.0) != epelectric);
brake("release", cab.Handle->GetPos(bh_RP), 20.0);
brake("full service", cab.Handle->GetPos(bh_FB), 40.0);
brake("release", cab.Handle->GetPos(bh_RP), 20.0);
scenario.check();
}
}
TEST_CASE("controls: a draisine pumped along by hand") {
// the pumping force, half a stroke forwards and half backwards, is below the resistance of a draisine
// standing, which stays where it is; a rolling one is kept going
for (auto const speed : {0.0, 10.0}) {
CAPTURE(speed);
scenario scenario{std::format("controls/draisine/{}", static_cast<int>(speed)), motion() + traction()};
scenario.add("dr2_v1/dr2", 1, test::consist::full_coupling, speed);
auto &cab{scenario.cab()};
cab.CabActivisation();
scenario.act("hand brake off");
test::release_brakes(cab);
for (int stroke = 0; stroke < 40; ++stroke) {
scenario.act("stroke");
cab.IncMainCtrl(1);
scenario.wait(0.5);
cab.DecMainCtrl(1);
scenario.wait(0.5);
}
CHECK((speed > 0.0) == (cab.Vel > 1.0));
scenario.act("coast");
scenario.wait(20.0);
scenario.check();
}
}

View File

@@ -0,0 +1,174 @@
#include "stdafx.h"
#include "doctest.h"
#include "consist.h"
#include "scenario.h"
#include "utilities/utilities.h"
// characterisation of the couplers between vehicles: coupling and uncoupling by hand (Attach, DettachStatus,
// Dettach), the automatic couplers coupling on contact, a broken coupler, and collisions which derail, on real
// vehicles, the moving ones recorded in golden traces (data/golden/couplers/)
using test::consist;
using test::definition;
using test::scenario;
using namespace test::channels;
TEST_CASE("couplers: coupled by hand, touching or apart, of the same kind or not") {
consist train;
auto &locomotive{train.add(definition::fiz("4e_v1/4e")).mover()};
// the wagon's buffers touch the locomotive's, the unit's car stands a metre away
auto &wagon{train.add(definition::fiz("3wl_v2/3wl"), 0, coupling::faux).mover()};
train.step();
// nothing to couple to, or nothing to couple
CHECK_FALSE(locomotive.Attach(end::rear, end::front, nullptr, coupling::coupler));
CHECK_FALSE(locomotive.Attach(end::rear, end::front, &wagon, coupling::faux));
// the hook first: the coupler alone, which sounds
locomotive.Couplers[end::rear].sounds = 0;
REQUIRE(locomotive.Attach(end::rear, end::front, &wagon, coupling::coupler));
CHECK(locomotive.Couplers[end::rear].Connected == &wagon);
CHECK(wagon.Couplers[end::front].Connected == &locomotive);
CHECK(wagon.Couplers[end::front].CouplingFlag == coupling::coupler);
CHECK(locomotive.Couplers[end::rear].sounds == sound::attachcoupler);
// then the hoses: only what changes sounds
locomotive.Couplers[end::rear].sounds = 0;
REQUIRE(locomotive.Attach(end::rear, end::front, &wagon, coupling::coupler | coupling::brakehose | coupling::mainhose));
CHECK(locomotive.Couplers[end::rear].sounds == (sound::attachbrakehose | sound::attachmainhose));
// and again, quietly, the other side naming the coupler by the one it's connected to (2)
locomotive.Couplers[end::rear].sounds = 0;
REQUIRE(wagon.Attach(end::front, 2, &locomotive, coupling::coupler | coupling::brakehose, false, false));
CHECK(locomotive.Couplers[end::rear].CouplingFlag == (coupling::coupler | coupling::brakehose));
CHECK(locomotive.Couplers[end::rear].sounds == 0);
// a screw coupler with the buffers pressed together comes apart whole
train.step();
CHECK(locomotive.DettachStatus(end::rear) < 0);
locomotive.Couplers[end::rear].sounds = 0;
CHECK(locomotive.Dettach(end::rear));
CHECK(locomotive.Couplers[end::rear].Connected == nullptr);
CHECK(wagon.Couplers[end::front].Connected == nullptr);
CHECK(locomotive.Couplers[end::rear].sounds == (sound::detach | sound::attachcoupler | sound::attachbrakehose));
CHECK(locomotive.DettachStatus(end::rear) == 0);
CHECK(locomotive.Dettach(end::rear));
}
TEST_CASE("couplers: a screw coupler uncoupled with the buffers apart, or with the hoses alone, or broken") {
struct state {
char const *name;
double gap; // between the buffers, the coupler stretched
int coupling;
bool damaged;
bool apart; // whether it comes apart whole
};
for (auto const &[name, gap, couplingtype, damaged, apart] : {
state{"stretched a little", 0.1, coupling::coupler | coupling::brakehose, false, false},
state{"stretched far", 0.3, coupling::coupler | coupling::brakehose, false, true},
state{"hoses alone", 0.1, coupling::brakehose, false, true},
state{"broken", 0.1, coupling::coupler | coupling::brakehose, true, true},
}) {
CAPTURE(name);
consist train;
auto &locomotive{train.add(definition::fiz("4e_v1/4e")).mover()};
auto &wagon{train.add(definition::fiz("3wl_v2/3wl"), 0, coupling::faux, 0.0, gap).mover()};
REQUIRE(locomotive.Attach(end::rear, end::front, &wagon, couplingtype, true));
if (damaged) {
SetFlag(locomotive.DamageFlag, dtrain_coupling);
}
train.step();
CHECK((locomotive.DettachStatus(end::rear) < 0) == apart);
CHECK(locomotive.Dettach(end::rear) == apart);
// what's left when it doesn't come apart is the coupler, the hoses taken off
CHECK(locomotive.Couplers[end::rear].CouplingFlag == (apart ? coupling::faux : coupling::coupler));
CHECK(wagon.Couplers[end::front].CouplingFlag == (apart ? coupling::faux : coupling::coupler));
}
// couplers of different kinds don't couple, unless forced
consist train;
auto &locomotive{train.add(definition::fiz("4e_v1/4e")).mover()};
auto &unit{train.add(definition::fiz("en57_v1/5bgb"), 0, coupling::faux).mover()};
train.step();
CHECK_FALSE(locomotive.Attach(end::rear, end::front, &unit, coupling::coupler));
CHECK(locomotive.Attach(end::rear, end::front, &unit, coupling::coupler, true));
}
TEST_CASE("couplers: automatic couplers coupling on contact") {
// a car running slowly into another from close by (its brakes are on), the automatic couplers of both ends
// set to couple what their AutomaticFlag says; without one, the hook alone
struct meeting {
char const *name;
char const *standing; // its rear coupler met
char const *running; // its front coupler meets
int coupling; // what they couple: what both flags allow, the control cable only of one kind
};
for (auto const &[name, standing, running, couplingtype] : {
meeting{"en57 to en57", "en57_v1/5bgb", "en57_v1/5bgaiii_led_spot", 39},
meeting{"impuls to en57", "en57_v1/5bgb", "impuls_v1/31we-a_pr", 39 & ~coupling::control},
meeting{"en57 without the flag", "en57_v1/5bgaiii_led_spot", "en57_v1/6ba_840", coupling::coupler},
}) {
CAPTURE(name);
scenario scenario{std::string{"couplers/automatic/"} + name, motion(), test::track{}};
scenario.add(standing, 0, consist::full_coupling);
scenario.add(running, 0, coupling::faux, 3.0, 0.2);
scenario.wait(10.0);
auto &first{scenario.vehicle(0)};
auto &second{scenario.vehicle(1)};
CHECK(first.Couplers[end::rear].CouplingFlag == couplingtype);
CHECK(second.Couplers[end::front].CouplingFlag == couplingtype);
// once coupled, not again until uncoupled; the vehicle which coupled marks it for the AI
CHECK_FALSE(first.Couplers[end::rear].AutomaticCouplingAllowed);
CHECK_FALSE(second.Couplers[end::front].AutomaticCouplingAllowed);
CHECK((TestFlag(first.AIFlag, sound::attachcoupler) || TestFlag(second.AIFlag, sound::attachcoupler)));
scenario.check();
}
}
TEST_CASE("couplers: a coupler broken, with its brake hose torn off") {
consist train;
auto &locomotive{train.add(definition::fiz("4e_v1/4e")).mover()};
auto &wagon{train.add(definition::fiz("3wl_v2/3wl")).mover()};
REQUIRE(locomotive.Couplers[end::rear].Connected == &wagon);
locomotive.damage_coupler(end::rear);
CHECK(TestFlag(locomotive.DamageFlag, dtrain_coupling));
CHECK(locomotive.AlarmChainFlag);
CHECK(locomotive.Couplers[end::rear].CouplingFlag == coupling::faux);
CHECK(wagon.Couplers[end::front].CouplingFlag == coupling::faux);
// the front coupler of the wagon, broken on its own: no hose to tear
wagon.damage_coupler(end::front);
CHECK(TestFlag(wagon.DamageFlag, dtrain_coupling));
CHECK_FALSE(wagon.AlarmChainFlag);
// articulated couplers don't break
consist articulated;
auto &unit{articulated.add(definition::fiz("impuls_v1/31we-a_pr")).mover()};
articulated.add(definition::fiz("impuls_v1/31we-b_pr"));
unit.damage_coupler(end::rear);
CHECK_FALSE(TestFlag(unit.DamageFlag, dtrain_coupling));
CHECK(unit.Couplers[end::rear].CouplingFlag != coupling::faux);
}
TEST_CASE("couplers: collisions fast enough to derail") {
// a locomotive running into a standing wagon from behind, and a wagon running backwards into a standing
// locomotive; over 15 km/h, weighed by the ratio of the masses, the vehicles derail
struct collision {
char const *name;
char const *frontpath;
double frontspeed;
char const *back;
double backspeed;
bool frontout;
bool backout;
};
for (auto const &[name, frontpath, frontspeed, back, backspeed, frontout, backout] : {
collision{"locomotive into a wagon at 60", "3wl_v2/3wl", 0.0, "4e_v1/4e", 60.0, true, false},
collision{"wagon backwards into a locomotive at 60", "3wl_v2/3wl", -60.0, "4e_v1/4e", 0.0, true, false},
collision{"locomotive into a locomotive at 25", "4e_v1/4e", 0.0, "4e_v1/4e", 25.0, true, true},
}) {
CAPTURE(name);
scenario scenario{std::string{"couplers/collision/"} + name, motion(), test::track{}};
scenario.add(frontpath, 0, consist::full_coupling, frontspeed);
scenario.add(back, 0, coupling::faux, backspeed, 20.0);
scenario.wait(10.0);
CHECK(TestFlag(scenario.vehicle(0).DamageFlag, dtrain_out) == frontout);
CHECK(TestFlag(scenario.vehicle(1).DamageFlag, dtrain_out) == backout);
scenario.check();
}
}

View File

@@ -1,6 +1,7 @@
#include "stdafx.h"
#include "doctest.h"
#include "vehicle.h"
#include "drive.h"
using test::definition;
using test::vehicle;
@@ -43,3 +44,108 @@ TEST_CASE("definition: rows of a table section reach the vehicle") {
CHECK(wagon->LightsPos == 1);
CHECK(wagon->Lights[end::front][1] == pc1);
}
TEST_CASE("definition: lines the loader skips or trims: comments, a hash anywhere, leading spaces, carriage returns") {
auto text{definition::load("wagon").text()};
// a commented line and one with a hash set the mass the wagon doesn't get; the rest, indented and ended with
// a carriage return, still load
auto const param{text.find("Param")};
REQUIRE(param != std::string::npos);
text.insert(param, "// Param: M=11111\nParam: M=22222 # a note\n ");
std::string crlf;
for (auto const c : text) {
if (c == '\n') {
crlf += '\r';
}
crlf += c;
}
TMoverParameters wagon(0.0, "wagon", "test", 0);
REQUIRE(wagon.LoadFIZ_Text(crlf));
CHECK(wagon.Mass == doctest::Approx(42200.0));
CHECK(wagon.Vmax == doctest::Approx(120.0));
CHECK(wagon.MaxBrakePress[3] == doctest::Approx(3.8));
}
TEST_CASE("definition: a compressor list, whose positions allow the compressor and scale its speed and limits") {
// none of the game's vehicles has one. its rows are the positions after the 0th, which changes nothing: whether
// the compressor may run (2 yes, 1 no, 0 as the switch says), its speed, and its lower and upper limits. the
// table holds whole numbers (int CompressorList[4][9]), so a factor of 0.9 is read as 0, and both limits
// drop to nothing
auto eu07{definition::fiz("4e_v1/4e")
.set("CompressorList", "Size", 3)
.set("CompressorList", "Default", 1)
.set("CompressorList", "rows", {{2, 2, 1, 1}, {1, 1, 1, 1}, {2, 1, 0.9, 0.9}})};
vehicle real{eu07, 0.0, "test", 1};
CHECK(real->CompressorListPosNo == 3);
CHECK(real->CompressorList[3][3] == 0);
auto driven{test::driving(real)};
test::track const line{0.15, 0.0, test::catenary_3kv};
REQUIRE(test::start_up(driven, line));
CHECK(real->CompressorListPos == 1);
test::drive(driven, line, 60.0);
CHECK(real->CompressorAllow);
CHECK(real->Compressor <= real->MaxCompressor + 0.05);
CHECK(real->Compressor >= real->MinCompressor - 0.05);
// the second position doesn't let it run
CHECK(real->ChangeCompressorPreset(2));
test::drive(driven, line, 2.0);
CHECK_FALSE(real->CompressorAllow);
CHECK_FALSE(real->CompressorFlag);
// the third lets it, under limits of nothing: with no span between them for the governor, the reservoir over
// the upper limit gets vented while the compressor runs on
CHECK(real->ChangeCompressorPreset(3));
auto const full{real->Compressor};
test::drive(driven, line, 10.0);
CHECK(real->CompressorAllow);
CHECK(real->CompressorFlag);
CHECK(real->Compressor < full - 1.0);
// no further than the last position
CHECK_FALSE(real->ChangeCompressorPreset(5));
CHECK(real->CompressorListPos == 3);
}
TEST_CASE("definition: the compressor's limits set apart for the rear cab") {
auto eu07{definition::fiz("4e_v1/4e").set("Brake", "MinCP_B", 5.0).set("Brake", "MaxCP_B", 6.0)};
for (auto const cab : {1, -1}) {
CAPTURE(cab);
vehicle real{eu07, 0.0, "test", cab};
auto driven{test::driving(real)};
test::track const line{0.15, 0.0, test::catenary_3kv};
REQUIRE(test::start_up(driven, line));
test::drive(driven, line, 120.0);
CHECK(real->CabDependentCompressor);
CHECK(real->MaxCompressor == doctest::Approx(cab > 0 ? 8.7 : 6.0));
CHECK(real->MinCompressor == doctest::Approx(cab > 0 ? 7.0 : 5.0));
CHECK(real->Compressor <= real->MaxCompressor + 0.05);
CHECK(real->Compressor >= real->MinCompressor - 0.05);
}
}
TEST_CASE("definition: the highest power of an induction motor drive by the speed (PmaxList)") {
// the drive's power limit (eimc_p_Pmax) follows the table, between its rows and, by the last two, past its end
auto eu47{definition::fiz("e186_v2/eu47").set("PmaxList", "Size", 3).set("PmaxList", "rows", {{0, 1000}, {50, 3000}, {100, 5600}})};
auto const table{[](double Speed) { return Speed < 50.0 ? 1000.0 + 40.0 * Speed : 3000.0 + 52.0 * (Speed - 50.0); }};
for (auto const speed : {25.0, 75.0, 150.0}) {
CAPTURE(speed);
vehicle real{eu47, speed, "test", 1};
auto driven{test::driving(real)};
test::track const line{0.0, 0.0, test::catenary_3kv};
REQUIRE(test::start_up(driven, line));
CHECK(real->eimc[eimc_p_Pmax] == doctest::Approx(table(real->Vel)).epsilon(0.01));
}
}
TEST_CASE("definition: the colour and the brightness of the headlights") {
// only the 214M's own file has the section, which the definitions here can't hold (see data/fiz/README.md)
vehicle railbus{definition::fiz("21xm_v1/214ma")
.set("Headlights", "LampRed", 248)
.set("Headlights", "LampGreen", 221)
.set("Headlights", "LampBlue", 111)
.set("Headlights", "DimmedMultiplier", 0.4)
.set("Headlights", "NormalMultiplier", 0.9)};
CHECK(railbus->refR == doctest::Approx(248.0));
CHECK(railbus->refG == doctest::Approx(221.0));
CHECK(railbus->refB == doctest::Approx(111.0));
CHECK(railbus->dimMultiplier == doctest::Approx(0.4));
CHECK(railbus->normMultiplier == doctest::Approx(0.9));
}

179
tests/unit/test_diesel.cpp Normal file
View File

@@ -0,0 +1,179 @@
#include "stdafx.h"
#include "doctest.h"
#include "scenario.h"
#include "drive.h"
#include "McZapkie/hamulce.h"
// characterisation of the diesel engines besides their drive, recorded in golden traces (data/golden/diesel/):
// the heat of the engine, its oil and its water circuits, which keep a too cold or too hot engine from starting
using test::scenario;
using namespace test::channels;
namespace {
std::vector<test::channel> heat() {
return {
{"Ts", [](auto const &M) { return M.dizel_heat.Ts; }},
{"To", [](auto const &M) { return M.dizel_heat.To; }},
{"Twy", [](auto const &M) { return M.dizel_heat.Twy; }},
{"Twy2", [](auto const &M) { return M.dizel_heat.Twy2; }},
{"PA", [](auto const &M) { return M.dizel_heat.PA ? 1.0 : 0.0; }},
{"WaterHeater", [](auto const &M) { return M.WaterHeater.is_active ? 1.0 : 0.0; }},
{"WaterPump", [](auto const &M) { return M.WaterPump.is_active ? 1.0 : 0.0; }},
{"zaluzje1", [](auto const &M) { return M.dizel_heat.zaluzje1 ? 1.0 : 0.0; }},
{"zaluzje2", [](auto const &M) { return M.dizel_heat.zaluzje2 ? 1.0 : 0.0; }},
};
}
} // namespace
TEST_CASE("diesel: an engine too cold to start, warmed by its heater") {
// at -10 degrees an engine with the lowest temperature of its water set (WaterMinTemperature) doesn't start,
// until the heater, which the driver switches on with the pumps, warms the water up; one without starts
struct engine {
char const *path;
bool starts; // cold
};
for (auto const &[path, starts] : {engine{"su45_v2/301d", false}, engine{"sm42_v1/6d1", false}, engine{"st44_v2/m62-1151", true}}) {
CAPTURE(path);
scenario scenario{std::string{"diesel/cold/"} + path, traction() + heat(), test::track{}};
scenario.add(path, 1);
auto &locomotive{scenario.cab()};
locomotive.dizel_HeatSet(-10.f);
CHECK(scenario.start_up() == starts);
CHECK(locomotive.dizel_heat.PA != starts);
CHECK(locomotive.WaterHeater.is_active);
scenario.act("heating");
scenario.wait(1200.0);
CHECK_FALSE(locomotive.dizel_heat.PA);
CHECK(locomotive.dizel_heat.Twy > 50.f);
CHECK(scenario.start_up());
scenario.check();
}
}
TEST_CASE("diesel: an engine started hot, and run at full power") {
// at 88 degrees the engines with a second water circuit are over its highest temperature
// (WaterAuxMaxTemperature) and don't start while it cools; the SM42 has none, starts and gets cooled by its
// fans and shutters at full power. alone: a train's pipe, leaking under 2.75 bar while the engine
// doesn't start, gets cut off the FV4a handle of an LSt locomotive (lock_old in UpdatePipePressure)
struct engine {
char const *path;
bool starts;
};
for (auto const &[path, starts] : {engine{"su45_v2/301d", false}, engine{"sm42_v1/6d1", true}, engine{"st44_v2/m62-1151", false}}) {
CAPTURE(path);
scenario scenario{std::string{"diesel/hot/"} + path, traction() + heat(), test::track{}};
scenario.add(path, 1);
auto &locomotive{scenario.cab()};
locomotive.dizel_HeatSet(88.f);
CHECK(scenario.start_up() == starts);
CHECK(locomotive.dizel_heat.PA != starts);
if (starts) {
scenario.get_going();
scenario.full_power(300.0);
CHECK(locomotive.Vel > 50.0);
CHECK(locomotive.dizel_heat.zaluzje1);
CHECK(locomotive.dizel_heat.Twy < locomotive.dizel_heat.water.config.temp_max);
}
scenario.check();
}
}
TEST_CASE("diesel: the 214M railbus's torque converter, gears and retarder") {
// the controller of the railbus (EIMCtrlType 3) runs from the air brake (0, 1) through the retarder (2) and
// neutral (3) to holding (4) and raising (5) the power; the torque converter locks up at speed and the
// gearbox shifts by itself
scenario scenario{"diesel/214m", motion() + traction() + brakes(), test::track{}};
scenario.add("21xm_v1/214ma", 1);
REQUIRE(scenario.start_up());
auto &railbus{scenario.cab()};
scenario.get_going();
scenario.full_power(60.0);
CHECK(railbus.MainCtrlPos == 5);
CHECK(railbus.eimic_real == doctest::Approx(1.0));
CHECK(railbus.hydro_TC_Lockup);
CHECK(railbus.ScndCtrlActualPos == railbus.ScndCtrlPosNo);
struct position {
double eimic;
bool retarder;
bool air;
};
auto number{railbus.MainCtrlPos};
for (auto const &[eimic, retarder, air] : {position{1.0, false, false}, position{0.0, false, false}, position{-1.0, true, false}, position{0.0, false, true}}) {
--number;
CAPTURE(number);
scenario.act(std::format("position {}", number));
railbus.DecMainCtrl(1);
scenario.wait(8.0);
CHECK(railbus.eimic_real == doctest::Approx(eimic));
CHECK((railbus.hydro_R_Fill > 0.99) == retarder);
CHECK((railbus.hydro_R_Torque > 1000.0) == retarder);
CHECK((railbus.BrakePress > 2.0) == air);
}
scenario.act("position 0");
railbus.DecMainCtrl(1);
scenario.wait(30.0);
CHECK(railbus.Vel == doctest::Approx(0.0));
// the gearbox shifted down as the railbus slowed
CHECK(railbus.ScndCtrlActualPos < railbus.ScndCtrlPosNo);
scenario.check();
}
TEST_CASE("diesel: the 214M railbus's speed control, up to the speed of a button and braking down to a lower one") {
// the second controller's first position takes the speed of the moment to hold; the buttons, the steps and
// the power limit change it. set lower than the speed, it brakes with the retarder (and under
// BrakeInterventionVel with the air); with the main controller off its top positions it stands by
scenario scenario{"diesel/214m speed control", motion() + traction() + brakes(), test::track{}};
scenario.add("21xm_v1/214ma", 1);
REQUIRE(scenario.start_up());
auto &railbus{scenario.cab()};
REQUIRE(railbus.SpeedCtrl);
scenario.get_going();
scenario.full_power(10.0);
scenario.act("speed control on");
REQUIRE(railbus.IncScndCtrl(1));
CHECK(railbus.SpeedCtrlUnit.IsActive);
CHECK(railbus.SpeedCtrlValue == doctest::Approx(std::round(railbus.Vel)).epsilon(0.02));
scenario.act("button 80");
railbus.SpeedCtrlButton(6);
CHECK(railbus.SpeedCtrlValue == doctest::Approx(80.0));
scenario.wait(90.0);
CHECK(railbus.Vel == doctest::Approx(80.0).epsilon(0.02));
CHECK_FALSE(railbus.SpeedCtrlUnit.Standby);
scenario.act("step down, up, power down and up");
railbus.SpeedCtrlDec();
CHECK(railbus.SpeedCtrlValue == doctest::Approx(75.0));
railbus.SpeedCtrlInc();
railbus.SpeedCtrlInc();
CHECK(railbus.SpeedCtrlValue == doctest::Approx(85.0));
CHECK(railbus.SpeedCtrlUnit.DesiredPower == doctest::Approx(1.0));
CHECK(railbus.SpeedCtrlPowerDec());
CHECK(railbus.SpeedCtrlUnit.DesiredPower == doctest::Approx(0.9));
CHECK(railbus.SpeedCtrlPowerInc());
CHECK(railbus.SpeedCtrlUnit.DesiredPower == doctest::Approx(1.0));
scenario.wait(20.0);
scenario.act("button 40");
railbus.SpeedCtrlButton(2);
scenario.wait(5.0);
// the retarder does it at this speed, the air isn't needed
CHECK(railbus.eimic_real == doctest::Approx(-1.0));
CHECK(railbus.hydro_R_Fill == doctest::Approx(1.0));
CHECK(railbus.BrakePress < 0.1);
CHECK_FALSE(railbus.SpeedCtrlUnit.BrakeInterventionBraking);
scenario.wait(60.0);
CHECK(railbus.Vel == doctest::Approx(40.0).epsilon(0.05));
scenario.act("main controller back, standing by");
railbus.DecMainCtrl(1);
railbus.DecMainCtrl(1);
railbus.DecMainCtrl(1);
scenario.wait(5.0);
CHECK(railbus.SpeedCtrlUnit.Standby);
scenario.act("speed control off");
railbus.DecScndCtrl(2);
scenario.wait(5.0);
CHECK_FALSE(railbus.SpeedCtrlUnit.IsActive);
scenario.check();
}

View File

@@ -0,0 +1,67 @@
#include "stdafx.h"
#include "doctest.h"
#include <algorithm>
#include "figures.h"
#include "vehicle.h"
// the key figures of the golden traces (figures.h): the parts of a run between the events, and the comparison
// with the recorded ones, which lets the last decimal and half a percent go and tells the rest in readable lines
using test::definition;
using test::vehicle;
TEST_CASE("figures: a run split into parts by the events, each from the last step of the part before") {
vehicle eu07{definition::fiz("4e_v1/4e")};
test::figures figures;
auto &mover{eu07.mover()};
mover.V = 10.0;
mover.PipePress = 5.0;
figures.sample(0.0, mover);
figures.mark("brake");
figures.mark("hard");
for (int i = 1; i <= 10; ++i) {
mover.V = 10.0 - i;
mover.DistCounter += 0.005;
mover.PipePress = 3.5;
mover.BrakePress = 0.4 * i;
figures.sample(i, mover);
}
figures.mark("release");
mover.PipePress = 4.0;
figures.sample(11.0, mover);
mover.PipePress = 5.0;
figures.sample(12.0, mover);
auto const lines{figures.lines()};
auto const has{[&lines](std::string const &Line) { return std::ranges::find(lines, Line) != lines.end(); }};
CHECK(lines.front() == "[start]");
// two events with nothing run between them make one part
CHECK(has("[brake, hard]"));
CHECK(has("duration = 10.00 s"));
CHECK(has("speed at the start = 36.0 km/h"));
CHECK(has("distance = 50.0 m"));
CHECK(has("stopped after = 10.00 s"));
CHECK(has("brake pipe lowest = 3.500 bar"));
CHECK(has("brake cylinders peak = 4.000 bar"));
CHECK(has("brake pipe back at 4.9 bar after = 2.00 s"));
// no current nor force anywhere in the run: left out of every part
CHECK_FALSE(has("motor current peak = 0 A"));
}
TEST_CASE("figures: compared within half a percent or the last decimal, the differences told by part") {
std::vector<std::string> const recorded{"[brake]", "distance = 412.3 m", "stopped after = 38.21 s", "brake cylinders peak = 3.800 bar"};
CHECK(test::figures::compare(recorded, recorded).empty());
CHECK(test::figures::compare(recorded, {"[brake]", "distance = 413.9 m", "stopped after = 38.22 s", "brake cylinders peak = 3.810 bar"}).empty());
auto const differences{test::figures::compare(recorded, {"[brake]", "distance = 398.0 m", "brake cylinders peak = 3.800 bar", "motor current peak = 10 A"})};
REQUIRE(differences.size() == 3);
CHECK(differences[0] == "[brake] distance: 398.0 m now, 412.3 m recorded");
CHECK(differences[1] == "[brake] stopped after: none now, 38.21 s recorded");
CHECK(differences[2] == "[brake] motor current peak: 10 A now, none recorded");
auto const parts{test::figures::compare(recorded, {"[brake hard]", "distance = 412.3 m"})};
REQUIRE(parts.size() == 1);
CHECK(parts[0] == "the parts differ from part 1: [brake hard] now, [brake] recorded");
}

183
tests/unit/test_fiz.cpp Normal file
View File

@@ -0,0 +1,183 @@
#include "stdafx.h"
#include "doctest.h"
#include "vehicle.h"
#include "golden.h"
#include "real_vehicles.h"
#include "fiz.h"
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include "utilities/parser.h"
using test::definition;
using test::vehicle;
// the real vehicle definitions in data/fiz load, as the game loads them, and give the parameters they gave
// when the traces got recorded: the loader (LoadFIZ_Parse and the parsing of every section) stays as it was
TEST_CASE("fiz: every real vehicle loads and keeps its parameters") {
for (auto const &path : test::real_vehicles()) {
CAPTURE(path);
test::reseed_random();
vehicle real{definition::fiz(path)};
test::trace trace{"fiz/" + path, test::channels::parameters(), 1};
trace.sample(0.0, real.mover());
// what every vehicle has, the rest of its parameters are in the golden trace
CHECK(real->Mass > 0.0);
CHECK(real->TotalMass >= real->Mass);
CHECK(real->Vmax > 0.0);
CHECK(real->NAxles > 0);
CHECK(real->WheelDiameter > 0.0);
CHECK(real->NPoweredAxles <= real->NAxles);
trace.check();
}
}
TEST_CASE("fiz: changes made by a test reach the vehicle") {
auto const eu07{definition::fiz("4e_v1/4e")};
CHECK(vehicle{eu07}->Mass == doctest::Approx(80000.0));
CHECK(vehicle{definition{eu07}.set("Param", "M", 90000)}->Mass == doctest::Approx(90000.0));
// a key the file doesn't have is added to its section
CHECK(vehicle{definition{eu07}.set("Brake", "MaxBP", 3.5)}->MaxBrakePress[3] == doctest::Approx(3.5));
// removing a key brings its default back
CHECK(vehicle{definition{eu07}.remove("Param", "Vmax")}->Vmax != doctest::Approx(125.0));
}
namespace {
// a section of the game's definition, as data/fiz keeps it
struct converted_section {
std::string name;
nlohmann::ordered_json keys = nlohmann::ordered_json::object();
nlohmann::ordered_json rows = nlohmann::ordered_json::array();
};
// the sections of the lines of the game's definition: the values of each section line, and the rows of a
// list until the line closing it. lines the loader skips, and the lines closing the lists, are left out
converted_section convert_section(test::fiz::section const &Section, std::string const &Line) {
converted_section converted{std::string{Section.name}};
for (auto const &key : test::fiz::keys(Line)) {
if (auto const value{test::fiz::value(key, Line)}; false == value.empty()) {
converted.keys[key] = value;
}
}
return converted;
}
nlohmann::ordered_json convert_row(std::string const &Line) {
auto row = nlohmann::ordered_json::array();
for (auto const &word : test::fiz::words(Line)) {
row.push_back(word);
}
return row;
}
std::vector<converted_section> convert(std::vector<std::string> const &Lines) {
std::vector<converted_section> result;
auto reading_rows{false};
for (auto const &line : Lines) {
if (auto const section{test::fiz::opened_by(line)}; section != nullptr) {
result.push_back(convert_section(*section, line));
reading_rows = section->list != 0;
continue;
}
auto const list{result.empty() ? nullptr : test::fiz::named(result.back().name)};
if (list == nullptr || list->end == nullptr) {
continue;
}
// an empty line closes the brake table of the Cntrl section, the other lists have lines of their own
if (std::string_view const end{list->end}; line.empty() ? end.empty() : (false == end.empty() && line.starts_with(end))) {
reading_rows = false;
continue;
}
if (reading_rows && false == line.empty()) {
result.back().rows.push_back(convert_row(line));
}
}
return result;
}
// the JSON of data/fiz: a section a line, a row of a list a line
std::string json_text(std::string const &Source, std::vector<converted_section> const &Sections) {
std::string text{"{\n \"_source\": " + nlohmann::ordered_json(Source).dump() + ",\n"};
text += R"( "_comment": "converted by test_fiz.cpp from the game's definition with the files it includes, keeping what the game's loader reads")";
for (auto const &section : Sections) {
text += ",\n " + nlohmann::ordered_json(section.name).dump() + ": {";
auto first{true};
for (auto const &[key, value] : section.keys.items()) {
text += (first ? "" : ", ") + nlohmann::ordered_json(key).dump() + ": " + value.dump();
first = false;
}
if (test::fiz::named(section.name)->list != 0) {
text += std::string{first ? "" : ", "} + "\"rows\": [";
for (std::size_t row = 0; row < section.rows.size(); ++row) {
text += (row == 0 ? "\n " : ",\n ") + section.rows[row].dump();
}
text += section.rows.empty() ? "]" : "\n ]";
}
text += "}";
}
return text + "\n}\n";
}
std::vector<std::string> split(std::string_view const List) {
std::vector<std::string> result;
std::size_t start{0};
while (start < List.size()) {
auto const end{std::min(List.find(',', start), List.size())};
if (end > start) {
result.emplace_back(List.substr(start, end - start));
}
start = end + 1;
}
return result;
}
} // namespace
// converts the game's definitions of the real vehicles to data/fiz, from the folder of the game's vehicles given
// in EU07_UNIT_TESTS_FIZ_SOURCE (dynamic/): the vehicles there already, and the ones named in EU07_UNIT_TESTS_FIZ_ADD
// (paths without pkp/ and the extension, separated by commas). the game's parser reads the file with its includes,
// the conversion keeps what the loader reads of it, and checks the loader reads the same of the converted one
TEST_CASE("fiz: conversion of the game's definitions, when asked for") {
auto const *source{std::getenv("EU07_UNIT_TESTS_FIZ_SOURCE")};
if (source == nullptr) {
return;
}
auto paths{test::real_vehicles()};
if (auto const *added{std::getenv("EU07_UNIT_TESTS_FIZ_ADD")}; added != nullptr) {
for (auto const &path : split(added)) {
paths.push_back(path);
}
}
auto const root{std::filesystem::path{source}};
for (auto const &path : paths) {
CAPTURE(path);
// the vehicles of the Polish railways are in pkp/, the others under their operators
auto file{root / (path + ".fiz")};
if (false == std::filesystem::exists(file)) {
file = root / "pkp" / (path + ".fiz");
}
REQUIRE(std::filesystem::exists(file));
cParser parser(file.filename().string(), cParser::buffer_FILE, file.parent_path().generic_string() + "/");
auto const original{test::fiz::lines(parser)};
auto const target{std::filesystem::path{EU07_UNIT_TESTS_DATA} / "fiz" / (path + ".json")};
std::filesystem::create_directories(target.parent_path());
{
std::ofstream output(target, std::ios::binary);
output << json_text(std::filesystem::relative(file, root).generic_string(), convert(original));
}
cParser converted(definition::fiz(path).text(), cParser::buffer_TEXT);
auto const expected{test::fiz::reading(original)};
auto const actual{test::fiz::reading(test::fiz::lines(converted))};
auto const mismatch{std::ranges::mismatch(expected, actual)};
if (mismatch.in1 != expected.end() || mismatch.in2 != actual.end()) {
std::filesystem::remove(target);
FAIL_CHECK("the loader reads the converted definition differently, at " << (mismatch.in1 != expected.end() ? *mismatch.in1 : "the end") << " against "
<< (mismatch.in2 != actual.end() ? *mismatch.in2 : "the end"));
}
}
}

View File

@@ -0,0 +1,34 @@
#include "stdafx.h"
#include "doctest.h"
#include "golden.h"
#include "vehicle.h"
// the invariants every golden trace checks (invariants.h) catch a vehicle in a state the simulation must
// never reach, once each, naming the time and the last event
using test::definition;
using test::vehicle;
TEST_CASE("invariants: a vehicle in an impossible state, reported once with its time and the last event") {
vehicle eu07{definition::fiz("4e_v1/4e")};
test::trace trace{"invariants/self test", test::channels::brakes()};
trace.sample(0.0, eu07.mover());
CHECK(trace.violations().empty());
trace.mark("overfilled");
eu07->BrakePress = 50.0;
trace.sample(0.01, eu07.mover());
trace.sample(0.02, eu07.mover());
REQUIRE(trace.violations().size() == 1);
auto const &violation{trace.violations().front()};
CHECK(violation.find("brake cylinders") != std::string::npos);
CHECK(violation.find("at 0.010 s") != std::string::npos);
CHECK(violation.find("after \"overfilled\"") != std::string::npos);
CHECK(violation.find("BrakePress=50") != std::string::npos);
eu07->BrakePress = 0.0;
eu07->V = std::nan("");
trace.sample(0.03, eu07.mover());
CHECK(trace.violations().size() > 1);
CHECK(trace.violations()[1].find("every value a number") != std::string::npos);
}

116
tests/unit/test_loads.cpp Normal file
View File

@@ -0,0 +1,116 @@
#include "stdafx.h"
#include "doctest.h"
#include "vehicle.h"
// characterisation of the loads of vehicles besides the loading by commands (test_commands.cpp): the load
// assigned to a vehicle (AssignLoad), with the pantographs of an electric vehicle set up by the "pantstate"
// load of a scenario, and the steps of loading and unloading (LoadingDone)
using test::definition;
using test::vehicle;
TEST_CASE("loads: the pantographs of an electric vehicle set up by the amount of its pantstate load") {
// bit 0 raises the front pantograph, bit 1 the rear one, bit 2 turns the vehicle round in the train first
// (DoubleTr), so the bits name the pantographs the other way round
struct setup {
int amount;
bool frontraised;
bool rearraised;
};
for (auto const &[amount, frontraised, rearraised] : {setup{0, false, false}, setup{1, true, false}, setup{2, false, true}, setup{3, true, true}, setup{5, false, true},
setup{6, true, false}}) {
CAPTURE(amount);
vehicle et42{definition::fiz("et42_v2/112e-a")};
CHECK(et42->AssignLoad("pantstate", static_cast<float>(amount)));
CHECK(et42->DoubleTr == ((amount & 4) != 0 ? -1 : 1));
CHECK(et42->Pantographs[end::front].valve.is_enabled == frontraised);
CHECK(et42->Pantographs[end::rear].valve.is_enabled == rearraised);
// the load itself stays as it was
CHECK(et42->LoadAmount == doctest::Approx(0.0));
}
// a vehicle without a current collector takes no such load
vehicle wagon{definition::fiz("3wl_v2/3wl")};
CHECK_FALSE(wagon->AssignLoad("pantstate", 3.f));
}
TEST_CASE("loads: a load assigned, of one kind at a time, up to the vehicle's capacity, and emptied") {
vehicle wagon{definition::fiz("3wl_v2/3wl")};
auto const empty{wagon->TotalMass};
REQUIRE(wagon->AssignLoad("coal", 10.f));
CHECK(wagon->LoadTypeChange);
CHECK(wagon->LoadType.name == "coal");
CHECK(wagon->TotalMass > empty);
// more of the same: the amount set, not added, and no more than the vehicle takes
wagon->LoadTypeChange = false;
REQUIRE(wagon->AssignLoad("coal", 1000.f));
CHECK_FALSE(wagon->LoadTypeChange);
CHECK(wagon->LoadAmount == doctest::Approx(wagon->MaxLoad));
// another kind can't be added to a load, nor can an unknown one be loaded
CHECK_FALSE(wagon->AssignLoad("ruda", 5.f));
CHECK(wagon->LoadType.name == "coal");
// emptied, with no name
REQUIRE(wagon->AssignLoad(""));
CHECK(wagon->LoadTypeChange);
CHECK(wagon->LoadAmount == doctest::Approx(0.0));
CHECK(wagon->LoadType.name.empty());
CHECK_FALSE(wagon->AssignLoad("unknown", 5.f));
CHECK(wagon->AssignLoad("ruda", 5.f));
}
TEST_CASE("loads: the steps of loading and unloading") {
// most of the game's wagons may be overloaded (OverLoadFactor), the 3WL not
vehicle wagon{definition::fiz("3wl_v2/3wl").set("Load", "OverLoadFactor", 1.5)};
REQUIRE(wagon->AssignLoad("coal", 10.f));
auto const capacity{wagon->MaxLoad * (1.0 + wagon->OverLoadFactor)};
SUBCASE("no speed of the change: done at once") {
CHECK(wagon->LoadingDone(0.0, "coal"));
CHECK(wagon->LoadStatus == 4);
}
SUBCASE("no load, or another one, named: done when it was") {
wagon->LoadStatus = 1;
CHECK_FALSE(wagon->LoadingDone(1.0, ""));
CHECK_FALSE(wagon->LoadingDone(1.0, "ruda"));
wagon->LoadStatus = 4;
CHECK(wagon->LoadingDone(1.0, ""));
}
SUBCASE("unloading, step by step, what was asked or all of it, the name going with the last of it") {
wagon->CommandIn.Value1 = 4.0; // asked to unload
wagon->LastLoadChangeTime = 2.0; // at 1 a second
CHECK_FALSE(wagon->LoadingDone(-1.0, "coal"));
CHECK(wagon->LoadStatus == 2);
CHECK(wagon->LoadAmount == doctest::Approx(8.0));
CHECK(wagon->LastLoadChangeTime == doctest::Approx(0.0));
// nothing done since the last step
CHECK_FALSE(wagon->LoadingDone(-1.0, "coal"));
CHECK(wagon->LoadAmount == doctest::Approx(8.0));
wagon->LastLoadChangeTime = 2.0;
CHECK(wagon->LoadingDone(-1.0, "coal"));
CHECK(wagon->LoadAmount == doctest::Approx(6.0));
wagon->CommandIn.Value1 = 100.0;
wagon->LastLoadChangeTime = 10.0;
CHECK(wagon->LoadingDone(-1.0, "coal"));
CHECK(wagon->LoadAmount == doctest::Approx(0.0));
CHECK(wagon->LoadType.name.empty());
}
SUBCASE("loading, step by step, up to what was asked or what the vehicle takes, slower when overloaded") {
wagon->CommandIn.Value1 = 5.0;
wagon->LastLoadChangeTime = 2.0;
CHECK_FALSE(wagon->LoadingDone(1.0, "coal"));
CHECK(wagon->LoadStatus == 1);
CHECK(wagon->LoadAmount == doctest::Approx(12.0));
wagon->LastLoadChangeTime = 3.0;
CHECK(wagon->LoadingDone(1.0, "coal"));
CHECK(wagon->LoadAmount == doctest::Approx(15.0));
wagon->CommandIn.Value1 = 1000.0;
wagon->LastLoadChangeTime = 1000.0;
CHECK(wagon->LoadingDone(1.0, "coal"));
CHECK(wagon->LoadAmount == doctest::Approx(capacity));
CHECK(capacity == doctest::Approx(2.5 * wagon->MaxLoad));
// over the vehicle's own capacity, the change goes at half the speed
wagon->CommandIn.Value1 = 1000.0;
wagon->LastLoadChangeTime = 4.0;
CHECK_FALSE(wagon->LoadingDone(-1.0, "coal"));
CHECK(wagon->LoadAmount == doctest::Approx(capacity - 2.0));
}
}

View File

@@ -0,0 +1,61 @@
#include "stdafx.h"
#include "doctest.h"
#include "vehicle.h"
// characterisation of what the game's panels read of a vehicle besides the gauges recorded in the traces: the
// description of its damage on the driver's panel (driveruipanels.cpp)
using test::definition;
using test::vehicle;
TEST_CASE("queries: the damage of a vehicle as the driver's panel describes it") {
vehicle locomotive{definition::fiz("4e_v1/4e")};
vehicle wagon{definition::fiz("3wl_v2/3wl")};
// a powered locomotive and a wagon read some of the flags differently
locomotive->Power = 2000.0;
REQUIRE(wagon->Power < 0.1);
struct damage {
int flag;
char const *locomotive;
char const *wagon;
};
for (auto const &[flag, locomotivetext, wagontext] : {
damage{0, "OK", "OK"},
damage{dtrain_thinwheel, "Thin wheel", "Load shifted"},
damage{dtrain_wheelwear, "Wheel wear", "Wheel wear"},
damage{dtrain_bearing, "Bearing damaged", "Bearing damaged"},
damage{dtrain_coupling, "Coupler broken", "Coupler broken"},
damage{dtrain_loaddamage, "Ventilator damaged", "Load damaged"},
damage{dtrain_loaddestroyed, "Engine damaged", "LOAD DESTROYED"},
damage{dtrain_axle, "Axle broken", "Axle broken"},
damage{dtrain_out, "DERAILED", "DERAILED"},
// the last flag of the list wins
damage{dtrain_bearing | dtrain_axle, "Axle broken", "Axle broken"},
damage{255, "WRECKED", "WRECKED"},
}) {
CAPTURE(flag);
locomotive->DamageFlag = flag;
wagon->DamageFlag = flag;
CHECK(locomotive->EngineDescription(0) == locomotivetext);
CHECK(wagon->EngineDescription(0) == wagontext);
}
// flat wheels count as worn ones
wagon->DamageFlag = 0;
wagon->WheelFlat = 6.0;
CHECK(wagon->EngineDescription(0) == "Wheel wear");
// nothing else is described
CHECK(wagon->EngineDescription(1) == "Invalid qualifier");
}
TEST_CASE("queries: the ammeters of a vehicle without power show the current of the ones it controls") {
vehicle unpowered{definition::fiz("en57_v1/6ba_840")};
vehicle powered{definition::fiz("en57_v1/5bgb")};
unpowered->Attach(end::rear, end::front, &powered.mover(), coupling::control, true, false);
powered->Power = 600.0;
powered->Itot = 321.7;
unpowered->Power = 0.0;
CHECK(unpowered->ShowCurrent(0) == doctest::Approx(321.0)); // whole amperes
powered->Power = 0.0;
CHECK(unpowered->ShowCurrent(0) == doctest::Approx(0.0));
}

View File

@@ -0,0 +1,270 @@
#include "stdafx.h"
#include <array>
#include "doctest.h"
#include "vehicle.h"
#include "drive.h"
#include "golden.h"
#include "real_vehicles.h"
#include "scenario.h"
#include "McZapkie/hamulce.h"
using test::definition;
using test::vehicle;
// characterisation of the drive of every real powered vehicle, driven alone from its front cab under a 3 kV
// catenary through test::drive_cycle(), in golden traces (data/golden/traction/). the cars of the electric
// units which take power or battery from the rest of their unit don't start alone: their traces record
// that, test_consist.cpp drives the whole units
TEST_CASE("traction: every real powered vehicle started up, driven to full power and braked to a stop") {
for (auto const &path : test::real_vehicles()) {
CAPTURE(path);
test::reseed_random();
vehicle real{definition::fiz(path), 0.0, "test", 1};
if (false == test::is_powered(real.mover())) {
continue;
}
using namespace test::channels;
test::trace trace{"traction/" + path, motion() + traction() + air() + brakes(), 100};
real.record(trace);
test::drive_cycle(test::driving(real), test::track{0.15, 0.0, test::catenary_3kv}, trace);
// every vehicle getting its line breaker closed runs off and is braked to a stop. the SM31 and the TEM2
// barely move (COVERAGE.md); the cars of a unit taking their power from another car don't start alone
if (real->Mains) {
auto const crawls{path == "sm31_v1/411d" || path == "tem2_v2/tem2a"};
CHECK((real->DistCounter > 0.2 /* km */) != crawls);
// but the road vehicle, which a minute of full service only slows down
CHECK((real->Vel < 0.5) != (path == "road/man/nl293"));
}
trace.check();
}
}
namespace {
using test::scenario;
using namespace test::channels;
void stop(scenario &Scenario) {
Scenario.notch_back(0);
Scenario.act("brake");
Scenario.cab().BrakeLevelSet(Scenario.cab().Handle->GetPos(bh_FB));
Scenario.wait(60.0);
}
} // namespace
TEST_CASE("traction: the line breaker opened by the catenary lost, and by too high a voltage") {
// the voltage relays of the series motors, and the induction motors' own limits: under MinV of the current
// collector, or over MaxV (with the overvoltage protection, OVP) and over MaxV + 200 V for the induction motors
for (auto const &[name, vehicles] : std::vector<std::pair<std::string, std::vector<char const *>>>{
{"eu07", {"4e_v1/4e"}}, {"et42", {"et42_v2/112e-a", "et42_v2/112e-b"}}, {"eu47", {"e186_v2/eu47"}}}) {
CAPTURE(name);
scenario scenario{"traction/catenary/" + name, motion() + traction()};
for (auto const *path : vehicles) {
scenario.add(path, scenario.train().size() == 0 ? 1 : 0);
}
scenario.add("bdhpumn_v3/bdhpumn");
REQUIRE(scenario.start_up());
scenario.get_going();
auto &controlling{scenario.driver().controlling};
scenario.full_power(10.0);
REQUIRE(controlling.Mains);
scenario.act("catenary lost");
scenario.line().voltage = 0.0;
scenario.wait(5.0);
CHECK_FALSE(controlling.Mains);
CHECK(controlling.Im == doctest::Approx(0.0));
scenario.act("catenary back");
scenario.line().voltage = test::catenary_3kv;
scenario.notch_back(0);
CHECK(scenario.start_up());
scenario.full_power(5.0);
scenario.act("overvoltage");
scenario.line().voltage = controlling.EnginePowerSource.CollectorParameters.MaxV + 300.0;
scenario.wait(5.0);
CHECK(controlling.Mains == (controlling.EngineType == TEngineType::ElectricSeriesMotor && false == controlling.EnginePowerSource.CollectorParameters.OVP));
scenario.line().voltage = test::catenary_3kv;
stop(scenario);
scenario.check();
}
}
TEST_CASE("traction: the ET42 at the high current setting, its halves in series, and its switched electrodynamic brake") {
for (auto const high : {false, true}) {
CAPTURE(high);
scenario scenario{std::string{"traction/et42/"} + (high ? "high current" : "low current"), motion() + traction() + brakes()};
scenario.add("et42_v2/112e-a", 1).add("et42_v2/112e-b").add("3wl_v2/3wl").add("3wl_v2/3wl").add("3wl_v2/3wl");
REQUIRE(scenario.start_up());
auto &et42{scenario.cab()};
scenario.act("current setting");
CHECK(et42.CurrentSwitch(high) == high);
// the relay takes the setting at the next step, with the controller at zero
scenario.get_going();
CHECK((et42.Imax == et42.ImaxHi) == high);
scenario.full_power(60.0);
// at the high current setting the controller stops at the last series position: the halves' motors stay
// in series, at half the voltage each, for a heavy start, and the train crawls
CHECK((et42.RList[et42.MainCtrlPos].Bn > 1) != high);
CHECK(et42.Vel > (high ? 30.0 : 90.0));
CHECK(et42.Vel < (high ? 50.0 : 110.0));
scenario.act("controller to zero");
while (et42.DecMainCtrl(2)) {
// down to zero
}
scenario.wait(2.0);
scenario.act("dynamic brake");
CHECK(et42.DynamicBrakeSwitch(true));
scenario.wait(5.0);
scenario.full_power(20.0);
// the controller sets the brake's strength. from the low setting's top speed, over 100 km/h, the motors'
// current goes over 300 A at the first position and the overload relay trips, leaving the train to roll;
// from the high setting's, under 40 km/h, the brake stops the train
CHECK(et42.FuseFlag != high);
CHECK((et42.Vel == doctest::Approx(0.0)) == high);
scenario.act("dynamic brake off");
scenario.notch_back(0);
et42.DynamicBrakeSwitch(false);
stop(scenario);
scenario.check();
}
}
TEST_CASE("traction: the field shunting relays of the diesel-electric locomotives, at full power and notched back") {
// each kind of the automatic relays (RelayType) the game's diesel-electrics have, run with a wagon up to full
// shunting (and past their top speed, which nothing limits) and notched back through the positions where the
// relays let the shunting go: by the current (0), by the speed alone (1, which keeps it at any position), by
// the current at the full position only (41), by the position and the speed (45, 46)
struct locomotive {
char const *path;
int relaytype;
std::array<int, 4> shunting; // after notching back to 11, 10, 7 and 3
};
for (auto const &[path, relaytype, shunting] : {locomotive{"cd/754_v1/754", 0, {3, 3, 3, 0}}, locomotive{"sm31_v1/411d", 1, {2, 2, 2, 2}},
locomotive{"sm40_v1/sm40", 41, {1, 1, 1, 1}}, locomotive{"su45_v2/301d", 45, {3, 2, 0, 0}},
locomotive{"su46_v2/303d", 46, {4, 2, 0, 0}}}) {
CAPTURE(path);
scenario scenario{std::string{"traction/relays/"} + path, motion() + traction()};
scenario.add(path, 1).add("bdhpumn_v3/bdhpumn");
REQUIRE(scenario.start_up());
auto &controlling{scenario.driver().controlling};
CHECK(controlling.RelayType == relaytype);
scenario.get_going();
scenario.full_power(120.0);
CHECK(controlling.ScndCtrlPos == controlling.ScndCtrlPosNo);
auto expected{shunting.begin()};
for (auto const position : {11, 10, 7, 3}) {
CAPTURE(position);
scenario.act(std::format("notch back to {}", position));
scenario.notch_back(position);
scenario.wait(15.0);
CHECK(controlling.ScndCtrlPos == *expected++);
}
stop(scenario);
scenario.check();
}
}
TEST_CASE("traction: the stages of the EW58's electrodynamic brake, set by the positions of its ep handle") {
// the FVE408's positions 1 to 5 give the ep brake's strength, whose whole part is the stage of the motors'
// brake (TUHEX): 1, 1, 2, 3, 3; over 100 km/h the motors brake at the first stage alone
scenario scenario{"traction/ew58 brake stages", motion() + traction() + brakes()};
for (auto const *car : {"ew58_v1/ew58-sa", "ew58_v1/ew58-d", "ew58_v1/ew58-sb"}) {
scenario.add(car, scenario.train().size() == 0 ? 1 : 0, test::consist::full_coupling, 115.0);
}
REQUIRE(scenario.start_up());
auto &ew58{scenario.cab()};
for (auto const &[position, stage] : {std::pair{1, 1}, std::pair{3, 2}, std::pair{4, 3}, std::pair{2, 1}, std::pair{5, 3}}) {
CAPTURE(position);
scenario.act(std::format("position {}", position));
ew58.BrakeLevelSet(position);
scenario.wait(6.0);
CHECK(ew58.TUHEX_StageActual == stage);
CHECK(ew58.DynamicBrakeFlag);
CHECK(ew58.Im < 0.0);
}
scenario.act("release");
ew58.BrakeLevelSet(ew58.Handle->GetPos(bh_RP));
scenario.wait(10.0);
CHECK(ew58.TUHEX_StageActual == 0);
scenario.check();
}
TEST_CASE("traction: the induction motor drive's speed control, its electrodynamic brake by the local brake, and at the voltage's limit") {
scenario scenario{"traction/eu47 drive", motion() + traction() + brakes()};
scenario.add("e186_v2/eu47", 1).add("bdhpumn_v3/bdhpumn").add("bdhpumn_v3/bdhpumn");
REQUIRE(scenario.start_up());
auto &eu47{scenario.cab()};
scenario.get_going();
// the second controller of the EU47 sets the speed to hold (SpeedCtrlType=Time): position 2 keeps the speed
// set, 3 raises it by 10 km/h every SpeedCtrlDelay, 4 sets the top speed, 1 lowers it, 0 drops it
REQUIRE(eu47.ScndCtrlPosNo == 4);
REQUIRE(eu47.SpeedCtrlTypeTime);
scenario.full_power(0.0);
scenario.act("speed up");
for (int i = 0; i < 3; ++i) {
eu47.IncScndCtrl(1);
}
scenario.wait(3.0);
scenario.act("hold");
eu47.DecScndCtrl(1);
scenario.wait(1.0);
CHECK(eu47.SpeedCtrlValue == doctest::Approx(60.0));
scenario.wait(60.0);
CHECK(eu47.Vel == doctest::Approx(60.0).epsilon(0.01));
scenario.act("slow down");
eu47.DecScndCtrl(1);
scenario.wait(1.5);
eu47.IncScndCtrl(1);
scenario.wait(1.0);
CHECK(eu47.SpeedCtrlValue == doctest::Approx(20.0));
// a lower speed set only takes the power off, the train rolls on
scenario.wait(40.0);
CHECK(eu47.Vel > 50.0);
scenario.act("top speed");
eu47.IncScndCtrl(2);
scenario.wait(5.0);
CHECK(eu47.SpeedCtrlValue == doctest::Approx(eu47.Vmax));
scenario.act("speed control off");
while (eu47.DecScndCtrl(1)) {
// down to zero
}
scenario.notch_back(0);
scenario.wait(2.0);
CHECK(eu47.SpeedCtrlValue == doctest::Approx(0.0));
// the local brake sets the flag of the electrodynamic brake, which keeps the train brake's air out of the
// cylinders, but the motors' brake follows the train brake alone: the local brake's air does the braking
scenario.act("local brake");
eu47.IncLocalBrakeLevel(LocalBrakePosNo);
scenario.wait(5.0);
CHECK(eu47.DynamicBrakeFlag);
CHECK(eu47.Im == doctest::Approx(0.0));
CHECK(eu47.BrakePress > 2.0);
scenario.act("local brake off");
eu47.DecLocalBrakeLevel(LocalBrakePosNo);
scenario.wait(10.0);
// braking back into the catenary: at its usual voltage, and at its highest, over the drive's MaxV, where
// the drive lets less of the current go back to it (Vadd)
for (auto const voltage : {test::catenary_3kv, eu47.EnginePowerSource.CollectorParameters.MaxV + 100.0}) {
CAPTURE(voltage);
scenario.line().voltage = voltage;
scenario.act(std::format("service brake at {} V", static_cast<int>(voltage)));
eu47.BrakeLevelSet(eu47.Handle->GetPos(bh_FB));
scenario.wait(6.0);
CHECK(eu47.DynamicBrakeFlag);
CHECK(eu47.Im < -100.0);
CHECK((eu47.Vadd > 1.0) == (voltage > eu47.EnginePowerSource.CollectorParameters.MaxV));
eu47.BrakeLevelSet(eu47.Handle->GetPos(bh_RP));
scenario.wait(6.0);
}
scenario.line().voltage = test::catenary_3kv;
scenario.act("line breaker off");
eu47.MainSwitch(false);
scenario.wait(5.0);
CHECK(eu47.Im == doctest::Approx(0.0));
stop(scenario);
scenario.check();
}

View File

@@ -1,60 +1,26 @@
#include "stdafx.h"
#include "vehicle.h"
#include "golden.h"
#include "real_vehicles.h"
#include "fiz.h"
#include "vehicle/DynObj.h"
#include <filesystem>
#include <fstream>
#include <stdexcept>
#include "utilities/Globals.h"
namespace test {
namespace {
struct section_format {
char separator; // the game's definitions end section names with either '.' or ':'
char const *table_end{nullptr}; // the line closing the rows of a table section
};
// the sections TMoverParameters::LoadFIZ() reads, see its issection() calls. a section missing here
// would be silently skipped by the loader, and the test would run on the defaults instead
std::map<std::string, section_format, std::less<>> const sections{
{"Param", {'.'}},
{"Load", {':'}},
{"Dimensions", {':'}},
{"Wheels", {':'}},
{"Brake", {':'}},
{"Doors", {':'}},
{"BuffCoupl", {'.'}},
{"BuffCoupl1", {'.'}},
{"BuffCoupl2", {'.'}},
{"TurboPos", {':'}},
{"Cntrl", {'.'}},
{"Blending", {':'}},
{"DCEMUED", {':'}},
{"SpringBrake", {':'}},
{"Light", {':'}},
{"Headlights", {':'}},
{"Clima", {':'}},
{"Power", {':'}},
{"SpeedControl", {':'}},
{"Engine", {':'}},
{"Switches", {':'}},
{"Security", {':'}},
{"Circuit", {':'}},
{"AI", {':'}},
{"LightsList", {':', "endL"}},
{"DimmerList", {':', "endDimmerList"}},
{"WiperList", {':', "endwl"}},
{"CompressorList", {':', "endCL"}},
{"RList", {':', "END-RL"}},
{"MotorParamTable0", {':', "END-MPT"}},
};
section_format const &format_of(std::string const &Section) {
auto const lookup{sections.find(Section)};
if (lookup == sections.end()) {
throw definition_error("unknown vehicle definition section " + Section + ", see tests/unit/vehicle.cpp");
fiz::section const &format_of(std::string const &Section) {
auto const section{fiz::named(Section)};
if (section == nullptr) {
throw definition_error("vehicle definition section " + Section + " isn't one the loader reads, see tests/unit/fiz.cpp");
}
return lookup->second;
return *section;
}
std::string value_text(nlohmann::ordered_json const &Value) {
@@ -67,9 +33,9 @@ std::string value_text(nlohmann::ordered_json const &Value) {
return Value.dump();
}
// the rows of a table section, each a list of values, and the line closing them
std::string table_text(std::string const &Section, section_format const &Format, nlohmann::ordered_json const &Rows) {
if (Format.table_end == nullptr) {
// the rows of a list section, each a list of values, and the line closing them
std::string table_text(std::string const &Section, fiz::section const &Format, nlohmann::ordered_json const &Rows) {
if (Format.end == nullptr) {
throw definition_error("vehicle definition section " + Section + " has no rows");
}
std::string text;
@@ -79,34 +45,47 @@ std::string table_text(std::string const &Section, section_format const &Format,
}
text += "\n";
}
return text + Format.table_end + "\n";
return text + Format.end + "\n";
}
} // namespace
definition definition::load(std::string const &Name) {
auto const path{std::string{EU07_UNIT_TESTS_DATA} + "/" + Name + ".json"};
std::ifstream file(path);
nlohmann::ordered_json sections_of(std::string const &Path) {
std::ifstream file(Path);
if (false == file.is_open()) {
throw definition_error("no test vehicle definition " + path);
throw definition_error("no test vehicle definition " + Path);
}
definition result;
// not brace-initialised: json{other} makes an array of other with GCC and Clang (the initializer list constructor wins)
auto const json = nlohmann::ordered_json::parse(file, nullptr, true, true);
if (false == json.is_object()) {
throw definition_error("test vehicle definition " + path + " isn't a JSON object");
throw definition_error("test vehicle definition " + Path + " isn't a JSON object");
}
auto result = nlohmann::ordered_json::object();
for (auto const &[section, keys] : json.items()) {
// keys starting with _ are comments
if (section.starts_with('_')) {
continue;
}
format_of(section);
result.m_sections[section] = keys;
result[section] = keys;
}
return result;
}
} // namespace
definition definition::load(std::string const &Name) {
definition result;
result.m_sections = sections_of(std::string{EU07_UNIT_TESTS_DATA} + "/" + Name + ".json");
return result;
}
definition definition::fiz(std::string const &Path) {
definition result;
result.m_sections = sections_of(std::string{EU07_UNIT_TESTS_DATA} + "/fiz/" + Path + ".json");
auto const slash{Path.rfind('/')};
result.m_typename = slash == std::string::npos ? Path : Path.substr(slash + 1);
return result;
}
definition &definition::set(std::string const &Section, std::string const &Key, nlohmann::ordered_json const &Value) {
format_of(Section);
m_sections[Section][Key] = Value;
@@ -143,52 +122,145 @@ std::string definition::text() const {
return text;
}
vehicle::vehicle(definition const &Definition, double Velocity, std::string const &Name) {
// the same steps as TDynamicObject::Init()
m_mover = std::make_unique<TMoverParameters>(Velocity, Name, Name, 0);
vehicle::vehicle(definition const &Definition, double Velocity, std::string const &Name, int Cab) : m_cab(Cab) {
// the same steps as TDynamicObject::Init(), for a vehicle set the usual way round (iDirection 1)
auto const typename_{Definition.type_name().empty() ? Name : Definition.type_name()};
m_mover = std::make_unique<TMoverParameters>(Velocity, typename_, Name, Cab);
if (false == m_mover->LoadFIZ_Text(Definition.text())) {
throw definition_error("the test vehicle definition didn't load");
}
m_mover->AssignLoad("", 0.f);
m_mover->ComputeMass();
auto const driveractive{Velocity != 0.0};
if (false == m_mover->CheckLocomotiveParameters(driveractive, Velocity >= 0.0 ? 1 : -1)) {
if (false == m_mover->CheckLocomotiveParameters(driveractive, (Velocity > 0 ? 1 : -1) * Cab)) {
throw definition_error("parameters mismatch in the test vehicle definition");
}
m_mover->MainCtrlPos = m_mover->MainCtrlNoPowerPos();
m_mover->LocalBrakePosA = 0.0;
m_mover->BrakeCtrlPos = static_cast<int>(std::floor(m_mover->Handle->GetPos(driveractive ? bh_RP : bh_NP)));
if (driveractive) {
m_mover->BrakeCtrlPos = static_cast<int>(std::floor(m_mover->Handle->GetPos(Cab == 0 ? bh_NP : bh_RP)));
// a driver keeps a vehicle on standby on its independent brake
if (Cab != 0 && m_mover->LocalBrake != TLocalBrake::ManualBrake && Velocity < 1.0) {
m_mover->IncLocalBrakeLevel(LocalBrakePosNo);
if (m_mover->EIMCtrlEmergency) {
m_mover->DecLocalBrakeLevel(1);
}
}
}
else {
m_mover->BrakeCtrlPos = static_cast<int>(std::floor(m_mover->Handle->GetPos(bh_NP)));
}
m_mover->BrakeLevelSet(m_mover->BrakeCtrlPos);
}
void vehicle::step(track const &Track, double Dt) {
compute_forces(Dt);
compute_movement(Track, Dt, TLocation{0.0, 0.0, 0.0});
}
void vehicle::compute_forces(double Dt) {
if (m_pipepressure >= 0.0) {
m_mover->Pipe->CreatePress(m_pipepressure);
begin_step();
TLocation const location{0.0, 0.0, 0.0};
for (int iteration = 1; iteration < Track.iterations; ++iteration) {
forces(Dt);
fast_move(Track, Dt, location);
}
m_mover->ComputeTotalForce(Dt);
forces(Dt);
move(Track, Dt, location, m_mover->PipePress);
}
double vehicle::compute_movement(track const &Track, double Dt, TLocation const &Location) {
namespace {
TTrackShape shape_of(track const &Track) {
TTrackShape shape;
shape.Len = 1000.0;
shape.dHtrack = Track.gradient;
return shape;
}
TTrackParam parameters_of(track const &Track, TMoverParameters const &Mover) {
TTrackParam param;
param.Width = 1.435;
param.Width = Track.width;
param.friction = Track.friction;
param.CategoryFlag = 1;
TTractionParam const traction;
// the vehicle's own kind of way: a road vehicle on rails derails as soon as it moves
if (Track.category != 0) {
param.CategoryFlag = Track.category;
}
else {
param.CategoryFlag = Mover.CategoryFlag != 0 ? Mover.CategoryFlag : 1;
}
return param;
}
} // namespace
double vehicle::fast_move(track const &Track, double Dt, TLocation const &Location) {
// TDynamicObject::FastUpdate(), the iterations of a frame before the last one
auto param{parameters_of(Track, *m_mover)};
return m_mover->FastComputeMovement(Dt, shape_of(Track), param, Location, TRotation{0.0, 0.0, 0.0});
}
void vehicle::begin_step() {
// the conditions the test keeps
if (m_pipepressure >= 0.0) {
m_mover->Pipe->CreatePress(m_pipepressure);
}
if (m_reservoirpressure >= 0.0 && m_mover->VeselVolume > 0.0) {
m_mover->CompressedVolume = m_reservoirpressure * m_mover->VeselVolume;
}
// vehicle_table::update(), before the forces
m_mover->ComputeConstans();
}
void vehicle::forces(double Dt) {
// TDynamicObject::UpdateForce()
m_mover->ComputeTotalForce(Dt);
}
double vehicle::move(track const &Track, double Dt, TLocation const &Location, double Trainpipe) {
auto const shape{shape_of(Track)};
auto const param{parameters_of(Track, *m_mover)};
TRotation const rotation{0.0, 0.0, 0.0};
return m_mover->ComputeMovement(Dt, Dt, shape, param, traction, Location, rotation);
// what TDynamicObject::Update() does before the movement. a raised pantograph touches the wire at once,
// the game animates its rise
auto &mover{*m_mover};
mover.PantFrontVolt = mover.Pantographs[end::front].is_active ? Track.voltage : 0.0;
mover.PantRearVolt = mover.Pantographs[end::rear].is_active ? Track.voltage : 0.0;
TTractionParam traction;
traction.TractionVoltage =
mover.EnginePowerSource.SourceType == TPowerSource::CurrentCollector ? std::max(std::abs(mover.PantRearVolt), std::abs(mover.PantFrontVolt)) : 0.95 * mover.EnginePowerSource.MaxVoltage;
traction.TractionFreq = 0;
traction.TractionMaxCurrent = 7500;
traction.TractionResistivity = 0.3;
mover.PantographVoltage = traction.TractionVoltage;
if (m_cab != 0) {
using enum TEngineType;
// the driver's own vehicle, which gets the average pressure of the train's brake pipe
mover.EqvtPipePress = Trainpipe;
if (auto const diesel{mover.EngineType == DieselEngine || mover.EngineType == DieselElectric}; diesel && mover.EIMCtrlType > 0) {
mover.CheckEIMIC(Dt);
if (mover.SpeedCtrl) {
mover.CheckSpeedCtrl(Dt);
}
mover.eimic_real = std::min(mover.eimic, mover.eimicSpeedCtrl);
}
if (mover.EngineType == ElectricInductionMotor) {
// the share of the electric and pneumatic brake over the train (TDynamicObject's MED) isn't here
mover.CheckEIMIC(Dt);
mover.CheckSpeedCtrl(Dt);
mover.eimic_real = std::min(mover.eimic, mover.eimicSpeedCtrl);
}
}
// the whole frame's time, of all its iterations
auto const frame{Dt * Track.iterations};
auto const distance{m_mover->ComputeMovement(Dt, frame, shape, param, traction, Location, rotation)};
m_time += frame;
if (m_trace != nullptr) {
m_trace->sample(m_time, *m_mover);
}
return distance;
}
void vehicle::run(double Seconds, track const &Track, double Dt) {
// whole steps: summing up Dt would make the step count depend on the rounding
auto const steps{std::lround(Seconds / Dt)};
// whole frames: summing up Dt would make their count depend on the rounding
auto const steps{std::lround(Seconds / (Dt * Track.iterations))};
for (long i = 0; i < steps; ++i) {
step(Track, Dt);
}
@@ -253,10 +325,12 @@ void pair::find_neighbours() {
void pair::step(track const &Track, double Dt) {
find_neighbours();
for (auto const &placed : m_vehicles) {
placed.owner->compute_forces(Dt);
placed.owner->begin_step();
placed.owner->forces(Dt);
}
for (auto &placed : m_vehicles) {
placed.position += placed.heading * placed.owner->compute_movement(Track, Dt, TLocation{placed.position, 0.0, 0.0});
auto const &mover{placed.owner->mover()};
placed.position += placed.heading * placed.owner->move(Track, Dt, TLocation{placed.position, 0.0, 0.0}, mover.PipePress);
}
}
@@ -278,4 +352,24 @@ int pair::coupling() const {
return m_vehicles[0].owner->mover().Couplers[m_vehicles[0].end].CouplingFlag;
}
void reseed_random() {
Global.random_engine.seed(1);
Global.local_random_engine.seed(1);
}
std::vector<std::string> const &real_vehicles() {
static std::vector<std::string> const paths{[] {
std::vector<std::string> result;
auto const root{std::filesystem::path{EU07_UNIT_TESTS_DATA} / "fiz"};
for (auto const &entry : std::filesystem::recursive_directory_iterator(root)) {
if (entry.is_regular_file() && entry.path().extension() == ".json") {
result.push_back(std::filesystem::relative(entry.path(), root).replace_extension().generic_string());
}
}
std::ranges::sort(result);
return result;
}()};
return paths;
}
} // namespace test

Some files were not shown because too many files have changed in this diff Show More