From 1ef465264ca9b21ea9d4b9f8f7f7e05e56a70288 Mon Sep 17 00:00:00 2001 From: Hirek193 Date: Wed, 14 Oct 2026 12:20:49 +0200 Subject: [PATCH] First implementation of graph vehicles --- CMakeLists.txt | 21 + McZapkie/MOVER.h | 112 +++ McZapkie/Mover.cpp | 782 +++++++++------ McZapkie/Mover_shadow.cpp | 161 +++ McZapkie/hamulce.cpp | 73 +- McZapkie/hamulce.h | 4 + application/application.cpp | 12 +- application/drivermode.cpp | 3 + application/driveruilayer.cpp | 2 + application/driveruilayer.h | 2 + input/command.cpp | 2 + input/command.h | 3 + input/drivermouseinput.cpp | 35 +- input/drivermouseinput.h | 6 + tools/vehicle_graph/README.md | 334 ++++++- .../vehicle_graph/examples/303e.expected.csv | 132 +++ tools/vehicle_graph/examples/303e.script | 33 + tools/vehicle_graph/examples/303e.vhg | 245 +++++ .../examples/vectron-logic.expected.csv | 506 +++++----- .../vehicle_graph/examples/vectron-logic.vhg | 38 +- .../examples/vectron.expected.csv | 92 ++ tools/vehicle_graph/examples/vectron.script | 25 + tools/vehicle_graph/examples/vectron.vhg | 254 +++++ tools/vehicle_graph/run_tests.py | 42 +- tools/vehicle_graph/tests/anim.expected.csv | 51 + tools/vehicle_graph/tests/anim.script | 9 + tools/vehicle_graph/tests/anim.vhg | 19 + tools/vehicle_graph/tests/body-type.vhg | 2 + tools/vehicle_graph/tests/body-unknown.vhg | 2 + tools/vehicle_graph/tests/brakes.expected.csv | 172 ++++ tools/vehicle_graph/tests/brakes.script | 7 + tools/vehicle_graph/tests/brakes.vhg | 36 + tools/vehicle_graph/tests/consist-short.vhg | 15 + .../vehicle_graph/tests/consist.expected.csv | 41 + tools/vehicle_graph/tests/consist.script | 17 + tools/vehicle_graph/tests/consist.vhg | 31 + .../tests/diesel-electric.expected.csv | 243 +++++ .../tests/diesel-electric.script | 12 + tools/vehicle_graph/tests/diesel-electric.vhg | 37 + .../tests/diesel-hydro.expected.csv | 243 +++++ tools/vehicle_graph/tests/diesel-hydro.script | 12 + tools/vehicle_graph/tests/diesel-hydro.vhg | 35 + .../tests/diesel-mech.expected.csv | 213 ++++ tools/vehicle_graph/tests/diesel-mech.script | 18 + tools/vehicle_graph/tests/diesel-mech.vhg | 32 + tools/vehicle_graph/tests/lua-bad-port.vhg | 2 + tools/vehicle_graph/tests/lua-load-loop.vhg | 2 + tools/vehicle_graph/tests/lua-no-block.vhg | 2 + tools/vehicle_graph/tests/lua-no-update.vhg | 2 + tools/vehicle_graph/tests/lua-port-twice.vhg | 2 + tools/vehicle_graph/tests/lua-sandbox.vhg | 2 + tools/vehicle_graph/tests/lua-syntax.vhg | 2 + .../vehicle_graph/tests/lua-unknown-param.vhg | 2 + tools/vehicle_graph/tests/lua.expected.csv | 41 + tools/vehicle_graph/tests/lua.script | 8 + tools/vehicle_graph/tests/lua.vhg | 21 + tools/vehicle_graph/tests/module-loop.vhg | 5 + tools/vehicle_graph/tests/module-missing.vhg | 3 + tools/vehicle_graph/tests/module-port.vhg | 4 + tools/vehicle_graph/tests/module-self.vhg | 3 + tools/vehicle_graph/tests/module.expected.csv | 18 + tools/vehicle_graph/tests/module.script | 4 + tools/vehicle_graph/tests/module.vhg | 23 + .../vehicle_graph/tests/modules/inverter.vhgm | 10 + tools/vehicle_graph/tests/modules/latch.vhgm | 25 + tools/vehicle_graph/tests/modules/self.vhgm | 2 + .../tests/multisystem.expected.csv | 47 + tools/vehicle_graph/tests/multisystem.script | 16 + tools/vehicle_graph/tests/multisystem.vhg | 59 ++ tools/vehicle_graph/tests/profile-version.vhg | 2 + .../vehicle_graph/tests/security.expected.csv | 64 ++ tools/vehicle_graph/tests/security.script | 6 + tools/vehicle_graph/tests/security.vhg | 30 + tools/vehicle_graph/tests/single.vhg | 2 + tools/vehicle_graph/tests/table-width.vhg | 2 + utilities/Globals.cpp | 13 + utilities/Globals.h | 3 + vehicle/Button.cpp | 15 + vehicle/Button.h | 3 + vehicle/Driver.cpp | 8 + vehicle/Driver.h | 1 + vehicle/DynObj.cpp | 190 +++- vehicle/DynObj.h | 30 + vehicle/Gauge.cpp | 23 + vehicle/Gauge.h | 4 + vehicle/Train.cpp | 29 +- vehicle/Train.h | 19 + vehicle/graph/blocks/cab.cpp | 59 +- vehicle/graph/blocks/cab.h | 117 ++- vehicle/graph/blocks/diesel.cpp | 920 ++++++++++++++++++ vehicle/graph/blocks/drive.cpp | 658 +++++++++++++ vehicle/graph/blocks/electric.cpp | 658 +++++++++++++ vehicle/graph/blocks/interfaces.cpp | 468 +++++++++ vehicle/graph/blocks/lua_block.cpp | 278 ++++++ vehicle/graph/blocks/module.cpp | 186 ++++ vehicle/graph/blocks/pneumatic.cpp | 801 +++++++++++++++ vehicle/graph/blocks/security.cpp | 149 +++ vehicle/graph/blocks/vehicle.cpp | 424 ++++++++ vehicle/graph/graph_block.cpp | 7 +- vehicle/graph/graph_block.h | 19 +- vehicle/graph/graph_body.cpp | 133 +++ vehicle/graph/graph_body.h | 144 +++ vehicle/graph/graph_catalog.cpp | 136 +++ vehicle/graph/graph_catalog.h | 25 + vehicle/graph/graph_format.cpp | 249 ++++- vehicle/graph/graph_format.h | 11 + vehicle/graph/graph_instance.cpp | 24 +- vehicle/graph/graph_instance.h | 21 + vehicle/graph/graph_lua.cpp | 592 +++++++++++ vehicle/graph/graph_lua.h | 84 ++ vehicle/graph/graph_preview.cpp | 534 ++++++++++ vehicle/graph/graph_preview.h | 22 + vehicle/graph/graph_standalone.cpp | 288 +++++- vehicle/graph/graph_template.cpp | 154 ++- vehicle/graph/graph_template.h | 23 +- vehicle/graph/graph_train.cpp | 276 ++++++ vehicle/graph/graph_types.cpp | 34 + vehicle/graph/graph_types.h | 14 +- vehicle/graph/graph_vehicle.cpp | 465 +++++++++ widgets/vehiclegraph.cpp | 101 ++ widgets/vehiclegraph.h | 32 + 121 files changed, 12292 insertions(+), 729 deletions(-) create mode 100644 McZapkie/Mover_shadow.cpp create mode 100644 tools/vehicle_graph/examples/303e.expected.csv create mode 100644 tools/vehicle_graph/examples/303e.script create mode 100644 tools/vehicle_graph/examples/303e.vhg create mode 100644 tools/vehicle_graph/examples/vectron.expected.csv create mode 100644 tools/vehicle_graph/examples/vectron.script create mode 100644 tools/vehicle_graph/examples/vectron.vhg create mode 100644 tools/vehicle_graph/tests/anim.expected.csv create mode 100644 tools/vehicle_graph/tests/anim.script create mode 100644 tools/vehicle_graph/tests/anim.vhg create mode 100644 tools/vehicle_graph/tests/body-type.vhg create mode 100644 tools/vehicle_graph/tests/body-unknown.vhg create mode 100644 tools/vehicle_graph/tests/brakes.expected.csv create mode 100644 tools/vehicle_graph/tests/brakes.script create mode 100644 tools/vehicle_graph/tests/brakes.vhg create mode 100644 tools/vehicle_graph/tests/consist-short.vhg create mode 100644 tools/vehicle_graph/tests/consist.expected.csv create mode 100644 tools/vehicle_graph/tests/consist.script create mode 100644 tools/vehicle_graph/tests/consist.vhg create mode 100644 tools/vehicle_graph/tests/diesel-electric.expected.csv create mode 100644 tools/vehicle_graph/tests/diesel-electric.script create mode 100644 tools/vehicle_graph/tests/diesel-electric.vhg create mode 100644 tools/vehicle_graph/tests/diesel-hydro.expected.csv create mode 100644 tools/vehicle_graph/tests/diesel-hydro.script create mode 100644 tools/vehicle_graph/tests/diesel-hydro.vhg create mode 100644 tools/vehicle_graph/tests/diesel-mech.expected.csv create mode 100644 tools/vehicle_graph/tests/diesel-mech.script create mode 100644 tools/vehicle_graph/tests/diesel-mech.vhg create mode 100644 tools/vehicle_graph/tests/lua-bad-port.vhg create mode 100644 tools/vehicle_graph/tests/lua-load-loop.vhg create mode 100644 tools/vehicle_graph/tests/lua-no-block.vhg create mode 100644 tools/vehicle_graph/tests/lua-no-update.vhg create mode 100644 tools/vehicle_graph/tests/lua-port-twice.vhg create mode 100644 tools/vehicle_graph/tests/lua-sandbox.vhg create mode 100644 tools/vehicle_graph/tests/lua-syntax.vhg create mode 100644 tools/vehicle_graph/tests/lua-unknown-param.vhg create mode 100644 tools/vehicle_graph/tests/lua.expected.csv create mode 100644 tools/vehicle_graph/tests/lua.script create mode 100644 tools/vehicle_graph/tests/lua.vhg create mode 100644 tools/vehicle_graph/tests/module-loop.vhg create mode 100644 tools/vehicle_graph/tests/module-missing.vhg create mode 100644 tools/vehicle_graph/tests/module-port.vhg create mode 100644 tools/vehicle_graph/tests/module-self.vhg create mode 100644 tools/vehicle_graph/tests/module.expected.csv create mode 100644 tools/vehicle_graph/tests/module.script create mode 100644 tools/vehicle_graph/tests/module.vhg create mode 100644 tools/vehicle_graph/tests/modules/inverter.vhgm create mode 100644 tools/vehicle_graph/tests/modules/latch.vhgm create mode 100644 tools/vehicle_graph/tests/modules/self.vhgm create mode 100644 tools/vehicle_graph/tests/multisystem.expected.csv create mode 100644 tools/vehicle_graph/tests/multisystem.script create mode 100644 tools/vehicle_graph/tests/multisystem.vhg create mode 100644 tools/vehicle_graph/tests/profile-version.vhg create mode 100644 tools/vehicle_graph/tests/security.expected.csv create mode 100644 tools/vehicle_graph/tests/security.script create mode 100644 tools/vehicle_graph/tests/security.vhg create mode 100644 tools/vehicle_graph/tests/single.vhg create mode 100644 tools/vehicle_graph/tests/table-width.vhg create mode 100644 vehicle/graph/blocks/diesel.cpp create mode 100644 vehicle/graph/blocks/drive.cpp create mode 100644 vehicle/graph/blocks/electric.cpp create mode 100644 vehicle/graph/blocks/interfaces.cpp create mode 100644 vehicle/graph/blocks/lua_block.cpp create mode 100644 vehicle/graph/blocks/module.cpp create mode 100644 vehicle/graph/blocks/pneumatic.cpp create mode 100644 vehicle/graph/blocks/security.cpp create mode 100644 vehicle/graph/blocks/vehicle.cpp create mode 100644 vehicle/graph/graph_body.cpp create mode 100644 vehicle/graph/graph_body.h create mode 100644 vehicle/graph/graph_lua.cpp create mode 100644 vehicle/graph/graph_lua.h create mode 100644 vehicle/graph/graph_preview.cpp create mode 100644 vehicle/graph/graph_preview.h create mode 100644 vehicle/graph/graph_train.cpp create mode 100644 vehicle/graph/graph_vehicle.cpp create mode 100644 widgets/vehiclegraph.cpp create mode 100644 widgets/vehiclegraph.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d8b2a3f3..9926e6e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,7 @@ option(ENABLE_MCC "Enable multicore compilation" ON) option(WITHDUMPGEN "Enable generating DMP files on crash" ON) option(ENABLE_AVX2 "Enable AVX2 instruction set (requires AVX2-capable CPU, Haswell/2013 or newer)" ON) option(ADD_VERSION_SUFFIX "Add date and commit hash to the executable name" ON) +option(VGRAPH_SHADOW_CHECK "Run the code extracted from Mover for the vehicle graph side by side with its original form, and log the differences" OFF) set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) # vcpkg init @@ -118,8 +119,23 @@ set(SOURCES "vehicle/graph/graph_template.cpp" "vehicle/graph/graph_instance.cpp" "vehicle/graph/graph_standalone.cpp" +"vehicle/graph/graph_lua.cpp" "vehicle/graph/blocks/logic.cpp" "vehicle/graph/blocks/cab.cpp" +"vehicle/graph/blocks/lua_block.cpp" +"vehicle/graph/graph_body.cpp" +"vehicle/graph/graph_vehicle.cpp" +"vehicle/graph/blocks/vehicle.cpp" +"vehicle/graph/blocks/pneumatic.cpp" +"vehicle/graph/blocks/security.cpp" +"vehicle/graph/blocks/electric.cpp" +"vehicle/graph/blocks/drive.cpp" +"vehicle/graph/blocks/diesel.cpp" +"vehicle/graph/blocks/interfaces.cpp" +"vehicle/graph/blocks/module.cpp" +"vehicle/graph/graph_train.cpp" +"vehicle/graph/graph_preview.cpp" +"McZapkie/Mover_shadow.cpp" "EU07.cpp" "export_e3d_standalone.cpp" "export_btf_standalone.cpp" @@ -253,6 +269,7 @@ set(SOURCES "widgets/vehicleparams.cpp" "widgets/trainingcard.cpp" "widgets/perfgraphs.cpp" +"widgets/vehiclegraph.cpp" "${DEPS_DIR}/glad/src/glad.c" "${DEPS_DIR}/dds-ktx/src/dds-ktx.c" @@ -331,6 +348,10 @@ if (WITH_OPENVR) set(SOURCES ${SOURCES} "vr/openvr_imp.cpp") endif() +if (VGRAPH_SHADOW_CHECK) + set(DEFINITIONS ${DEFINITIONS} "VGRAPH_SHADOW_CHECK") +endif() + if (WITH_LUA) set(DEFINITIONS ${DEFINITIONS} "WITH_LUA") set(SOURCES ${SOURCES} "scripting/lua.cpp") diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index accbbbaa..863374a1 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1233,6 +1233,9 @@ struct TCoupling power_coupling power_24v; int sounds{0}; // sounds emitted by the coupling devices + // end cocks of the brake pipe and the feed pipe; operated by the vehicle graph (if.pg, if.pz), always open for the .fiz vehicles + bool BrakePipeCock{true}; + bool FeedPipeCock{true}; bool Render = false; /*ABu: czy rysowac jak zaczepiony sprzeg*/ std::string control_type; // abstraction of control coupling interface and communication standard @@ -1296,6 +1299,92 @@ struct inverter bool Failure_Const = false; }; +namespace vgraph +{ +class graph_instance; +class graph_template; +} // namespace vgraph + +// brake equipment of a vehicle, created from its parameters. shared by CheckLocomotiveParameters and the vehicle graph +struct brake_valve_setup +{ + TBrakeValve valve{TBrakeValve::NoValve}; + double max_brake_press{0.0}; // MaxBrakePress[3] + double tare_max_brake_press{0.0}; // MaxBrakePress[1] + double cylinder_radius{0.0}; + double cylinder_stroke{0.0}; + double valve_volume{0.0}; + int cylinder_count{0}; + int delays{0}; + int method{0}; + int axles{0}; + int blocks_per_axle{0}; + double mass{0.0}; + double load_brake_mass{0.0}; // MBPM + double rapid_mult{1.0}; + int valve_size{0}; + std::string valve_params; +}; +std::shared_ptr make_brake_valve(brake_valve_setup const &Setup); +std::shared_ptr make_driver_handle(TBrakeHandle Handle); +std::shared_ptr make_local_brake_handle(TBrakeHandle Handle, double MaxPressure, bool Emu); + +// force of the pneumatic brake rigging, from the cylinder pressure. shared by TMoverParameters::BrakeForce and the vehicle graph +struct brake_rigging_setup +{ + double press_to_force{0.0}; // P2FTrans, kN/bar + double cylinder_spring{0.0}; // BrakeCylSpring + double cylinder_mult{0.0}; // BrakeCylMult[0] + double slack_adjuster{0.0}; // BrakeSlckAdj + double rigging_efficiency{1.0}; // BrakeRigEff + int cylinder_count{0}; // BrakeCylNo + int axles{0}; // braked axles + int blocks_per_axle{0}; // NBpA +}; +// force pressing one brake shoe, kN. K: additional force (manual, spring brake), kN; Ntotal: shoe force with hysteresis, +// kept between the calls +double brake_rigging_shoe_force(brake_rigging_setup const &Setup, double K, double BrakePress, double &Ntotal); + +// series dc motor equations, shared by TMoverParameters and the vehicle graph +// current of the motors in traction for supply voltage U and motor revolutions n, Mn motors in series, Rz total resistance +double series_motor_current(double U, double n, double Mn, double Rz, TMotorParameters const &Param); +// torque of a motor for current I +double series_motor_momentum(double I, TMotorParameters const &Param); + +// induction motor with inverter, shared by TMoverParameters and the vehicle graph +struct induction_motor_inputs +{ + double demand{0.0}; // PosRatio: set value of the force, -1..1 + double rate{4.0}; // rate factor of the set value ramp (tmp) + bool dynamic_brake{false}; // DynamicBrakeFlag + double engine_voltage{0.0}; + double enrot{0.0}; // motor revolutions, rev/s + double vel{0.0}; // km/h + double v{0.0}; // m/s, signed + int dir_absolute{0}; + int dir_active{0}; + int powered_axles{0}; + double inverters_ratio{1.0}; + double ratio{1.0}; // transmission ratio + double efficiency{1.0}; + double wheel_diameter{1.0}; + bool log_force{false}; // EIMCLogForce + double max_voltage{0.0}; // EnginePowerSource.CollectorParameters.MaxV + double dt{0.0}; +}; +struct induction_motor_outputs +{ + double Im{0.0}; + double Itot{0.0}; + double EnginePower{0.0}; + double Mm{0.0}; + double Mw{0.0}; + double Fw{0.0}; + double Ft{0.0}; +}; +// eimc: constants of the drive, eimv: values of the drive (in/out), eimv_pr and Vadd: state kept between the calls +void induction_motor_update(induction_motor_inputs const &Input, double const *eimc, double *eimv, double &eimv_pr, double &Vadd, induction_motor_outputs &Output); + class TMoverParameters { // Ra: wrapper na kod pascalowy, przejmujący jego funkcje Q: 20160824 - juz nie wrapper a klasa bazowa :) private: @@ -2419,6 +2508,8 @@ class TMoverParameters void CompressorCheck(double dt); /*wlacza, wylacza kompresor, laduje zbiornik*/ void UpdatePantVolume(double dt); // Ra void UpdateScndPipePressure(double dt); + // air flow between the feed pipes of the vehicle and its neighbours; returns the flow into the own pipe + double ScndPipeExchange(double dt); void UpdateSpringBrake(double dt); double GetDVc(double dt); @@ -2543,6 +2634,27 @@ class TMoverParameters /*funkcje ladujace pliki opisujace pojazd*/ bool LoadFIZ(std::string chkpath); // Q 20160717 bool LoadChkFile(std::string chkpath); bool CheckLocomotiveParameters(bool ReadyFlag, int Dir); + // vehicle graph (vehicle/graph): the vehicle is defined by a .vhg file in place of the .fiz. LoadGraph reads the file, + // fills the body and the parameters of the equipment the blocks use, and is followed by CheckLocomotiveParameters as LoadFIZ + bool LoadGraph(std::string const &File); + // as above, for a graph already loaded; used also by the standalone runner (-graph-run). entries of the body the graph + // doesn't give are taken from the body sections of Fizfile, when it's given and exists + bool AssignGraph(std::shared_ptr const &Template, std::string const &Fizfile = {}); + // subsystems of a graph vehicle, run by compute_movement_ in place of the .fiz ones + void compute_movement_graph_(double const Deltatime); + // tractive and brake force of a graph vehicle, from the last update of its graph + void graph_forces(double &Tractive, double &Brake) const; + // null for the .fiz vehicles + std::shared_ptr Graph; +#ifdef VGRAPH_SHADOW_CHECK + // the code extracted to the functions shared with the vehicle graph, kept in its original form (Mover_shadow.cpp). + // each call of an extracted function is repeated with the original code on copies of the state, and the + // differences are logged + double shadow_series_motor_current(double U, double n, double Mn, double Rz, int SP) const; + double shadow_brake_rigging_shoe_force(double K, double &Ntotal) const; + void shadow_induction_motor(double PosRatio, double tmp, double dt, double *Eimv, double &Eimvpr, double &Vaddcopy, induction_motor_outputs &Output) const; + void shadow_compare(char const *What, double Original, double Extracted) const; +#endif std::string EngineDescription(int what) const; private: diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index ff60c27c..e0f7b670 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -278,6 +278,27 @@ double TableInterpolation(std::map &Map, double Parameter) return lower->second + (Parameter - lower->first) * ratio; } +// current of the series motors in traction, for supply voltage U and revolutions n; shared with the vehicle graph +double series_motor_current(double const U, double const n, double const Mn, double const Rz, TMotorParameters const &Param) +{ + double MotorCurrent; + double const U1 = U + Mn * n * Param.fi0 * Param.fi; // napiecie z korekta + double const Isf = Sign(U1) * Param.Isat; + double const Delta = square(Isf * Rz + Mn * Param.fi * n - U1) + 4.0 * U1 * Isf * Rz; // 105 * 1.67 + Mn * 140.9 * 20.532 - U1 + if (U > 0) + MotorCurrent = (U1 - Isf * Rz - Mn * Param.fi * n + std::sqrt(Delta)) / (2.0 * Rz); + else + MotorCurrent = (U1 - Isf * Rz - Mn * Param.fi * n - std::sqrt(Delta)) / (2.0 * Rz); + return MotorCurrent; +} + +// torque of a series motor for current I; shared with the vehicle graph +double series_motor_momentum(double const I, TMotorParameters const &Param) +{ + // Momentum:=mfi*I*(1-1.0/(Abs(I)/mIsat+1)); + return Param.mfi * I * (abs(I) / (abs(I) + Param.mIsat) - Param.mfi0); +} + // ************************************************************************************************* // Q: 20160716 // Obliczanie natężenie prądu w silnikach @@ -290,11 +311,10 @@ double TMoverParameters::Current(double n, double U) // jest takze mozliwosc uszkodzenia silnika wskutek nietypowych parametrow double R, MotorCurrent; - double Rz, Delta, Isf; + double Rz; double Mn; // przujmuje int, ale dla poprawnosci obliczeń double Bn; int SP = 0; - double U1; // napiecie z korekta MotorCurrent = 0; // i dzialanie hamulca ED w EP09 @@ -398,23 +418,12 @@ double TMoverParameters::Current(double n, double U) } else { - U1 = U + Mn * n * motor_param(SP).fi0 * motor_param(SP).fi; - // writepaslog("U1 ", FloatToStr(U1)); - // writepaslog("Isat ", FloatToStr(MotorParam[SP].Isat)); - // writepaslog("fi ", FloatToStr(MotorParam[SP].fi)); - Isf = Sign(U1) * motor_param(SP).Isat; - // writepaslog("Isf ", FloatToStr(Isf)); - Delta = square(Isf * Rz + Mn * motor_param(SP).fi * n - U1) + 4.0 * U1 * Isf * Rz; // 105 * 1.67 + Mn * 140.9 * 20.532 - U1 - // DeltaQ = Isf * Rz + Mn * MotorParam[SP].fi * n - U1 + 4 * U1 * Isf * Rz; - // writepaslog("Delta ", FloatToStr(Delta)); - // writepaslog("DeltaQ ", FloatToStr(DeltaQ)); - // writepaslog("U ", FloatToStr(U)); if (Mains) { - if (U > 0) - MotorCurrent = (U1 - Isf * Rz - Mn * motor_param(SP).fi * n + std::sqrt(Delta)) / (2.0 * Rz); - else - MotorCurrent = (U1 - Isf * Rz - Mn * motor_param(SP).fi * n - std::sqrt(Delta)) / (2.0 * Rz); + MotorCurrent = series_motor_current(U, n, Mn, Rz, motor_param(SP)); +#ifdef VGRAPH_SHADOW_CHECK + shadow_compare("series_motor_current", shadow_series_motor_current(U, n, Mn, Rz, SP), MotorCurrent); +#endif } else MotorCurrent = 0; @@ -1467,8 +1476,11 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap // koniec procedury, tu nastepuja dodatkowe procedury pomocnicze compute_movement_(dt); - // security system - SecuritySystemCheck(dt1); + // security system; a graph vehicle has it as a block of its graph + if (nullptr == Graph) + { + SecuritySystemCheck(dt1); + } return d; }; @@ -1540,6 +1552,12 @@ double TMoverParameters::FastComputeMovement(double dt, const TTrackShape &Shape // updates shared between 'fast' and regular movement computation methods void TMoverParameters::compute_movement_(double const Deltatime) { + if (Graph) + { + // the subsystems of a graph vehicle are blocks of its graph + compute_movement_graph_(Deltatime); + return; + } // sprawdzanie i ewentualnie wykonywanie->kasowanie poleceń if (LoadStatus > 0) // czas doliczamy tylko jeśli trwa (roz)ładowanie @@ -4781,41 +4799,11 @@ void TMoverParameters::UpdateScndPipePressure(double dt) } const double Spz = 0.5067; - TMoverParameters *c; - double dv1, dv2, dV; + double dV; UpdateSpringBrake(dt); - dv1 = 0; - dv2 = 0; - - // sprzeg 1 - if (Couplers[0].Connected != nullptr) - if (TestFlag(Couplers[0].CouplingFlag, ctrain_scndpneumatic)) - { - c = Couplers[0].Connected; // skrot - dv1 = 0.5 * dt * PF(ScndPipePress, c->ScndPipePress, Spz * 0.75); - if (dv1 * dv1 > 0.00000000000001) - c->switch_physics(true); - c->Pipe2->Flow(-dv1); - } - // sprzeg 2 - if (Couplers[1].Connected != nullptr) - if (TestFlag(Couplers[1].CouplingFlag, ctrain_scndpneumatic)) - { - c = Couplers[1].Connected; // skrot - dv2 = 0.5 * dt * PF(ScndPipePress, c->ScndPipePress, Spz * 0.75); - if (dv2 * dv2 > 0.00000000000001) - c->switch_physics(true); - c->Pipe2->Flow(-dv2); - } - if (Couplers[1].Connected != nullptr && Couplers[0].Connected != nullptr) - if (TestFlag(Couplers[0].CouplingFlag, ctrain_scndpneumatic) && TestFlag(Couplers[1].CouplingFlag, ctrain_scndpneumatic)) - { - dV = 0.00025 * dt * PF(Couplers[0].Connected->ScndPipePress, Couplers[1].Connected->ScndPipePress, Spz * 0.25); - Couplers[0].Connected->Pipe2->Flow(+dV); - Couplers[1].Connected->Pipe2->Flow(-dV); - } + auto const dv12{ScndPipeExchange(dt)}; Pipe2->Flow(Hamulec->GetHPFlow(ScndPipePress, dt)); // NOTE: condition disabled to allow the air flow from the main hose to the main tank as well @@ -4825,7 +4813,7 @@ void TMoverParameters::UpdateScndPipePressure(double dt) CompressedVolume += dV / 1000.0; Pipe2->Flow(-dV); } - Pipe2->Flow(dv1 + dv2); + Pipe2->Flow(dv12); Pipe2->Act(); ScndPipePress = Pipe2->P(); @@ -4837,6 +4825,49 @@ void TMoverParameters::UpdateScndPipePressure(double dt) } } +// air flow between the feed pipes of this vehicle and its neighbours. the flows into the neighbours are applied to their +// pipes; the returned flow into the own pipe is left to the caller +double TMoverParameters::ScndPipeExchange(double dt) +{ + const double Spz = 0.5067; + TMoverParameters *c; + double dv1, dv2, dV; + + dv1 = 0; + dv2 = 0; + + // sprzeg 1 + if (Couplers[0].Connected != nullptr) + if (TestFlag(Couplers[0].CouplingFlag, ctrain_scndpneumatic) && Couplers[0].FeedPipeCock && Couplers[0].Connected->Couplers[Couplers[0].ConnectedNr].FeedPipeCock) + { + c = Couplers[0].Connected; // skrot + dv1 = 0.5 * dt * PF(ScndPipePress, c->ScndPipePress, Spz * 0.75); + if (dv1 * dv1 > 0.00000000000001) + c->switch_physics(true); + c->Pipe2->Flow(-dv1); + } + // sprzeg 2 + if (Couplers[1].Connected != nullptr) + if (TestFlag(Couplers[1].CouplingFlag, ctrain_scndpneumatic) && Couplers[1].FeedPipeCock && Couplers[1].Connected->Couplers[Couplers[1].ConnectedNr].FeedPipeCock) + { + c = Couplers[1].Connected; // skrot + dv2 = 0.5 * dt * PF(ScndPipePress, c->ScndPipePress, Spz * 0.75); + if (dv2 * dv2 > 0.00000000000001) + c->switch_physics(true); + c->Pipe2->Flow(-dv2); + } + if (Couplers[1].Connected != nullptr && Couplers[0].Connected != nullptr) + if (TestFlag(Couplers[0].CouplingFlag, ctrain_scndpneumatic) && TestFlag(Couplers[1].CouplingFlag, ctrain_scndpneumatic) && Couplers[0].FeedPipeCock && + Couplers[1].FeedPipeCock) + { + dV = 0.00025 * dt * PF(Couplers[0].Connected->ScndPipePress, Couplers[1].Connected->ScndPipePress, Spz * 0.25); + Couplers[0].Connected->Pipe2->Flow(+dV); + Couplers[1].Connected->Pipe2->Flow(-dV); + } + + return dv1 + dv2; +} + // ************************************************************************************************* // yB: 20190906 // Aktualizacja ciśnienia w hamulcu sprezynowym @@ -4881,7 +4912,7 @@ double TMoverParameters::GetDVc(double dt) dv2 = 0; // sprzeg 1 if (Couplers[0].Connected != nullptr) - if (TestFlag(Couplers[0].CouplingFlag, ctrain_pneumatic)) + if (TestFlag(Couplers[0].CouplingFlag, ctrain_pneumatic) && Couplers[0].BrakePipeCock && Couplers[0].Connected->Couplers[Couplers[0].ConnectedNr].BrakePipeCock) { //*0.85 c = Couplers[0].Connected; // skrot //0.08 //e/D * L/D = e/D^2 * L dv1 = 0.5 * dt * PF(PipePress, c->PipePress, Spg / (1.0 + 0.015 / Spg * Dim.L)); @@ -4891,7 +4922,7 @@ double TMoverParameters::GetDVc(double dt) } // sprzeg 2 if (Couplers[1].Connected != nullptr) - if (TestFlag(Couplers[1].CouplingFlag, ctrain_pneumatic)) + if (TestFlag(Couplers[1].CouplingFlag, ctrain_pneumatic) && Couplers[1].BrakePipeCock && Couplers[1].Connected->Couplers[Couplers[1].ConnectedNr].BrakePipeCock) { c = Couplers[1].Connected; // skrot dv2 = 0.5 * dt * PF(PipePress, c->PipePress, Spg / (1.0 + 0.015 / Spg * Dim.L)); @@ -5059,7 +5090,11 @@ void TMoverParameters::ComputeTotalForce(double dt) LastRelayTime += dt; } - if (EngineType == TEngineType::ElectricSeriesMotor) // potem ulepszyc! pantogtrafy! + if (Graph) + { + // EngineVoltage of a graph vehicle is set by its drive blocks + } + else if (EngineType == TEngineType::ElectricSeriesMotor) // potem ulepszyc! pantogtrafy! { // Ra 2014-03: uwzględnienie kierunku jazdy w napięciu na silnikach, a powinien być zdefiniowany nawrotnik EngineVoltage = Mains ? EnginePowerSourceVoltage() : 0.0; if (CabActive == 0) @@ -5076,15 +5111,23 @@ void TMoverParameters::ComputeTotalForce(double dt) EngineVoltage = Power > 1.0 ? std::max(GetTrainsetHighVoltage(), PantographVoltage) : 0.0; } - FTrain = Power > 0 ? TractionForce(dt) : 0; double FT_factor = 1.0; - if (EngineType == TEngineType::ElectricInductionMotor && InvertersRatio > 0.0) + if (Graph) { - FT_factor = 1.0 / InvertersRatio; - FTrain *= FT_factor; + // sums of the drive.axle and br.rigging blocks, from the last update of the graph + graph_forces(FTrain, Fb); } + else + { + FTrain = Power > 0 ? TractionForce(dt) : 0; + if (EngineType == TEngineType::ElectricInductionMotor && InvertersRatio > 0.0) + { + FT_factor = 1.0 / InvertersRatio; + FTrain *= FT_factor; + } - Fb = BrakeForce(RunningTrack); + Fb = BrakeForce(RunningTrack); + } // poslizg auto Fwheels{FTrain - Fb * Sign(V)}; if (Vel > 0.1 // crude trap, to prevent braked stationary vehicles from passing fb > mass * adhesive test @@ -5190,6 +5233,32 @@ double TMoverParameters::BrakeForceP(double press, double velocity) // Q: 20160713 // oblicza siłę na styku koła i szyny // ************************************************************************************************* +// force pressing one brake shoe, kN, from the cylinder pressure and the additional force K (manual, spring brake); +// shared with the vehicle graph. Ntotal keeps the hysteresis of the shoe force between the calls +double brake_rigging_shoe_force(brake_rigging_setup const &Setup, double K, double const BrakePress, double &Ntotal) +{ + double u = (BrakePress * Setup.press_to_force - Setup.cylinder_spring) * Setup.cylinder_mult - Setup.slack_adjuster; + if (u * Setup.rigging_efficiency > Ntotal) // histereza na nacisku klockow + Ntotal = u * Setup.rigging_efficiency; + else + { + u = (BrakePress * Setup.press_to_force - Setup.cylinder_spring) * Setup.cylinder_mult - Setup.slack_adjuster; + if (u * (2.0 - Setup.rigging_efficiency) < Ntotal) // histereza na nacisku klockow + Ntotal = u * (2.0 - Setup.rigging_efficiency); + } + + auto const NBrakeAxles{Setup.axles}; + auto const NBpA{Setup.blocks_per_axle}; + + if (NBrakeAxles * NBpA > 0) + { + if (Ntotal > 0) // nie luz + K += Ntotal; // w kN + K *= static_cast(Setup.cylinder_count) / (NBrakeAxles * static_cast(NBpA)); // w kN na os + } + return K; +} + double TMoverParameters::BrakeForce(TTrackParam const &Track) { @@ -5221,24 +5290,26 @@ double TMoverParameters::BrakeForce(TTrackParam const &Track) if (SpringBrake.IsReady) K += std::max(0.0, SpringBrake.MinForcePressure - SpringBrake.Cylinder->P()) * SpringBrake.MaxBrakeForce; - u = (BrakePress * P2FTrans - BrakeCylSpring) * BrakeCylMult[0] - BrakeSlckAdj; - if (u * BrakeRigEff > Ntotal) // histereza na nacisku klockow - Ntotal = u * BrakeRigEff; - else - { - u = (BrakePress * P2FTrans - BrakeCylSpring) * BrakeCylMult[0] - BrakeSlckAdj; - if (u * (2.0 - BrakeRigEff) < Ntotal) // histereza na nacisku klockow - Ntotal = u * (2.0 - BrakeRigEff); - } - + brake_rigging_setup rigging; + rigging.press_to_force = P2FTrans; + rigging.cylinder_spring = BrakeCylSpring; + rigging.cylinder_mult = BrakeCylMult[0]; + rigging.slack_adjuster = BrakeSlckAdj; + rigging.rigging_efficiency = BrakeRigEff; + rigging.cylinder_count = BrakeCylNo; + rigging.axles = NAxles; + rigging.blocks_per_axle = NBpA; +#ifdef VGRAPH_SHADOW_CHECK + auto shadowntotal{Ntotal}; + auto const shadowk{shadow_brake_rigging_shoe_force(K, shadowntotal)}; +#endif + K = brake_rigging_shoe_force(rigging, K, BrakePress, Ntotal); +#ifdef VGRAPH_SHADOW_CHECK + shadow_compare("brake_rigging_shoe_force", shadowk, K); + shadow_compare("brake_rigging_shoe_force Ntotal", shadowntotal, Ntotal); +#endif auto const NBrakeAxles{NAxles}; - if (NBrakeAxles * NBpA > 0) - { - if (Ntotal > 0) // nie luz - K += Ntotal; // w kN - K *= static_cast(BrakeCylNo) / (NBrakeAxles * static_cast(NBpA)); // w kN na os - } if (BrakeSystem == TBrakeSystem::Pneumatic || BrakeSystem == TBrakeSystem::ElectroPneumatic) { u = Hamulec->GetFC(Vel, K); @@ -6417,51 +6488,54 @@ double TMoverParameters::TractionForce(double dt) eimv_pr = 0; } - eimv_pr += std::max(std::min(PosRatio - eimv_pr, 0.02), -0.02) * 12 * tmp /*2{+4*byte(PosRatio= 0) - Vadd *= 1.0 - 2.0 * dt; - else if (std::abs(EngineVoltage) < EnginePowerSource.CollectorParameters.MaxV) - Vadd *= 1.0 - dt; - else - Vadd = std::max(Vadd * (1.0 - 0.2 * dt), 0.007 * (std::abs(EngineVoltage) - (EnginePowerSource.CollectorParameters.MaxV - 100))); - Itot = eimv[eimv_Ipoj] * (0.01 + std::min(0.99, 0.99 - Vadd)); - - EnginePower = abs(eimv[eimv_Ic] * eimv[eimv_U] * NPoweredAxles) / 1000; // power inverters auto const tmpV{std::abs(eimv[eimv_fp])}; auto const useFFEDList = FFEDListSize > 0 && DynamicBrakeFlag; @@ -6518,11 +6564,6 @@ double TMoverParameters::TractionForce(double dt) InverterFrequency = 0.0; } - Mm = eimv[eimv_M] * DirAbsolute; - Mw = Mm * Transmision.Ratio * Transmision.Efficiency; - Fw = Mw * 2.0 / WheelDiameter; - Ft = Fw * NPoweredAxles * InvertersRatio; - eimv[eimv_Fr] = DirAbsolute * Ft / 1000; } // mains else { @@ -6573,6 +6614,92 @@ double TMoverParameters::TractionForce(double dt) return Ft; } +// induction motor and inverter of the ElectricInductionMotor drive: from the set value of the force to the torque and the +// tractive force; shared with the vehicle graph +void induction_motor_update(induction_motor_inputs const &Input, double const *eimc, double *eimv, double &eimv_pr, double &Vadd, induction_motor_outputs &Output) +{ + double tmp; + eimv_pr += std::max(std::min(Input.demand - eimv_pr, 0.02), -0.02) * 12 * Input.rate /*2{+4*byte(Input.demand= 0) + Vadd *= 1.0 - 2.0 * Input.dt; + else if (std::abs(Input.engine_voltage) < Input.max_voltage) + Vadd *= 1.0 - Input.dt; + else + Vadd = std::max(Vadd * (1.0 - 0.2 * Input.dt), 0.007 * (std::abs(Input.engine_voltage) - (Input.max_voltage - 100))); + Output.Itot = eimv[eimv_Ipoj] * (0.01 + std::min(0.99, 0.99 - Vadd)); + + Output.EnginePower = abs(eimv[eimv_Ic] * eimv[eimv_U] * Input.powered_axles) / 1000; + Output.Mm = eimv[eimv_M] * Input.dir_absolute; + Output.Mw = Output.Mm * Input.ratio * Input.efficiency; + Output.Fw = Output.Mw * 2.0 / Input.wheel_diameter; + Output.Ft = Output.Fw * Input.powered_axles * Input.inverters_ratio; + eimv[eimv_Fr] = Input.dir_absolute * Output.Ft / 1000; +} + // ************************************************************************************************* // Q: 20160713 // Obliczenie predkości obrotowej kół??? @@ -6761,8 +6888,10 @@ double TMoverParameters::Momentum(double I) if (!(RList[MainCtrlActualPos].ScndAct == 255)) SP = RList[MainCtrlActualPos].ScndAct; - // Momentum:=mfi*I*(1-1.0/(Abs(I)/mIsat+1)); - return motor_param(SP).mfi * I * (abs(I) / (abs(I) + motor_param(SP).mIsat) - motor_param(SP).mfi0); +#ifdef VGRAPH_SHADOW_CHECK + shadow_compare("series_motor_momentum", motor_param(SP).mfi * I * (abs(I) / (abs(I) + motor_param(SP).mIsat) - motor_param(SP).mfi0), series_motor_momentum(I, motor_param(SP))); +#endif + return series_motor_momentum(I, motor_param(SP)); } // ************************************************************************************************* @@ -11741,6 +11870,176 @@ TEngineType TMoverParameters::LoadFIZ_EngineDecode(std::string const &Engine) // Q: 20160717 // ************************************************************************************************* +// ************************************************************************************************* +// brake equipment factories, shared by CheckLocomotiveParameters and the vehicle graph +// ************************************************************************************************* +std::shared_ptr make_brake_valve(brake_valve_setup const &Setup) +{ + std::shared_ptr result; + switch (Setup.valve) + { + case TBrakeValve::W: + case TBrakeValve::K: + { + WriteLog("XBT W, K"); + result = std::make_shared(Setup.max_brake_press, Setup.cylinder_radius, Setup.cylinder_stroke, Setup.valve_volume, Setup.cylinder_count, Setup.delays, Setup.method, Setup.axles, Setup.blocks_per_axle); + if (Setup.load_brake_mass < 2) // jesli przystawka wazaca + result->SetLP(0, Setup.max_brake_press, 0); + else + result->SetLP(Setup.mass, Setup.load_brake_mass, Setup.tare_max_brake_press); + break; + } + case TBrakeValve::KE: + { + WriteLog("XBT WKE"); + result = std::make_shared(Setup.max_brake_press, Setup.cylinder_radius, Setup.cylinder_stroke, Setup.valve_volume, Setup.cylinder_count, Setup.delays, Setup.method, Setup.axles, Setup.blocks_per_axle); + result->SetRM(Setup.rapid_mult); + if (Setup.load_brake_mass < 2) // jesli przystawka wazaca + result->SetLP(0, Setup.max_brake_press, 0); + else + result->SetLP(Setup.mass, Setup.load_brake_mass, Setup.tare_max_brake_press); + break; + } + case TBrakeValve::NESt3: + case TBrakeValve::ESt3: + case TBrakeValve::ESt3AL2: + case TBrakeValve::ESt4: + { + WriteLog("XBT NESt3, ESt3, ESt3AL2, ESt4"); + result = std::make_shared(Setup.max_brake_press, Setup.cylinder_radius, Setup.cylinder_stroke, Setup.valve_volume, Setup.cylinder_count, Setup.delays, Setup.method, Setup.axles, Setup.blocks_per_axle); + static_cast(result.get())->SetSize(Setup.valve_size, Setup.valve_params); + if (Setup.load_brake_mass < 2) // jesli przystawka wazaca + result->SetLP(0, Setup.max_brake_press, 0); + else + result->SetLP(Setup.mass, Setup.load_brake_mass, Setup.tare_max_brake_press); + break; + } + case TBrakeValve::LSt: + { + WriteLog("XBT LSt"); + result = std::make_shared(Setup.max_brake_press, Setup.cylinder_radius, Setup.cylinder_stroke, Setup.valve_volume, Setup.cylinder_count, Setup.delays, Setup.method, Setup.axles, Setup.blocks_per_axle); + result->SetRM(Setup.rapid_mult); + break; + } + case TBrakeValve::EStED: + { + WriteLog("XBT EStED"); + result = std::make_shared(Setup.max_brake_press, Setup.cylinder_radius, Setup.cylinder_stroke, Setup.valve_volume, Setup.cylinder_count, Setup.delays, Setup.method, Setup.axles, Setup.blocks_per_axle); + result->SetRM(Setup.rapid_mult); + if (Setup.load_brake_mass < 2) + { + // jesli przystawka wazaca + result->SetLP(0, Setup.max_brake_press, 0); + } + else + { + result->SetLP(Setup.mass, Setup.load_brake_mass, Setup.tare_max_brake_press); + } + break; + } + case TBrakeValve::EP2: + { + WriteLog("XBT EP2"); + result = std::make_shared(Setup.max_brake_press, Setup.cylinder_radius, Setup.cylinder_stroke, Setup.valve_volume, Setup.cylinder_count, Setup.delays, Setup.method, Setup.axles, Setup.blocks_per_axle); + result->SetLP(Setup.mass, Setup.load_brake_mass, Setup.tare_max_brake_press); + break; + } + case TBrakeValve::EP1: + { + WriteLog("XBT EP1"); + result = std::make_shared(Setup.max_brake_press, Setup.cylinder_radius, Setup.cylinder_stroke, Setup.valve_volume, Setup.cylinder_count, Setup.delays, Setup.method, Setup.axles, Setup.blocks_per_axle); + result->SetLP(Setup.mass, Setup.load_brake_mass, Setup.tare_max_brake_press); + result->SetRM(Setup.rapid_mult); + break; + } + case TBrakeValve::CV1: + { + WriteLog("XBT CV1"); + result = std::make_shared(Setup.max_brake_press, Setup.cylinder_radius, Setup.cylinder_stroke, Setup.valve_volume, Setup.cylinder_count, Setup.delays, Setup.method, Setup.axles, Setup.blocks_per_axle); + break; + } + case TBrakeValve::CV1_L_TR: + { + WriteLog("XBT CV1_L_T"); + result = std::make_shared(Setup.max_brake_press, Setup.cylinder_radius, Setup.cylinder_stroke, Setup.valve_volume, Setup.cylinder_count, Setup.delays, Setup.method, Setup.axles, Setup.blocks_per_axle); + break; + } + default: + result = std::make_shared(Setup.max_brake_press, Setup.cylinder_radius, Setup.cylinder_stroke, Setup.valve_volume, Setup.cylinder_count, Setup.delays, Setup.method, Setup.axles, Setup.blocks_per_axle); + } + return result; +} + +std::shared_ptr make_driver_handle(TBrakeHandle const Handle) +{ + std::shared_ptr result; + switch (Handle) + { + case TBrakeHandle::FV4a: + result = std::make_shared(); + break; + case TBrakeHandle::MHZ_EN57: + case TBrakeHandle::MHZ_K8P: + result = std::make_shared(); + break; + case TBrakeHandle::FVel6: + result = std::make_shared(); + break; + case TBrakeHandle::FVE408: + result = std::make_shared(); + break; + case TBrakeHandle::testH: + result = std::make_shared(); + break; + case TBrakeHandle::M394: + result = std::make_shared(); + break; + case TBrakeHandle::Knorr: + result = std::make_shared(); + break; + case TBrakeHandle::St113: + result = std::make_shared(); + break; + case TBrakeHandle::MHZ_K5P: + result = std::make_shared(); + break; + case TBrakeHandle::MHZ_6P: + result = std::make_shared(); + break; + default: + result = std::make_shared(); + } + return result; +} + +std::shared_ptr make_local_brake_handle(TBrakeHandle const Handle, double const MaxPressure, bool const Emu) +{ + std::shared_ptr result; + switch (Handle) + { + case TBrakeHandle::FD1: + { + result = std::make_shared(); + result->Init(MaxPressure); + if (Emu) + { + + dynamic_cast(result.get())->SetSpeed(3.5); + } + break; + } + case TBrakeHandle::Knorr: + { + result = std::make_shared(); + result->Init(MaxPressure); + break; + } + default: + result = std::make_shared(); + } + return result; +} + bool TMoverParameters::CheckLocomotiveParameters(bool ReadyFlag, int Dir) { WriteLog("check locomotive parameters..."); @@ -11793,161 +12092,34 @@ bool TMoverParameters::CheckLocomotiveParameters(bool ReadyFlag, int Dir) } // WriteLog("BVV = " + FloatToStr(BrakeVVolume)); - switch (BrakeValve) { - case TBrakeValve::W: - case TBrakeValve::K: - { - WriteLog("XBT W, K"); - Hamulec = std::make_shared(MaxBrakePress[3], BrakeCylRadius, BrakeCylDist, BrakeVVolume, BrakeCylNo, BrakeDelays, BrakeMethod, NAxles, NBpA); - if (MBPM < 2) // jesli przystawka wazaca - Hamulec->SetLP(0, MaxBrakePress[3], 0); - else - Hamulec->SetLP(Mass, MBPM, MaxBrakePress[1]); - break; - } - case TBrakeValve::KE: - { - WriteLog("XBT WKE"); - Hamulec = std::make_shared(MaxBrakePress[3], BrakeCylRadius, BrakeCylDist, BrakeVVolume, BrakeCylNo, BrakeDelays, BrakeMethod, NAxles, NBpA); - Hamulec->SetRM(RapidMult); - if (MBPM < 2) // jesli przystawka wazaca - Hamulec->SetLP(0, MaxBrakePress[3], 0); - else - Hamulec->SetLP(Mass, MBPM, MaxBrakePress[1]); - break; - } - case TBrakeValve::NESt3: - case TBrakeValve::ESt3: - case TBrakeValve::ESt3AL2: - case TBrakeValve::ESt4: - { - WriteLog("XBT NESt3, ESt3, ESt3AL2, ESt4"); - Hamulec = std::make_shared(MaxBrakePress[3], BrakeCylRadius, BrakeCylDist, BrakeVVolume, BrakeCylNo, BrakeDelays, BrakeMethod, NAxles, NBpA); - static_cast(Hamulec.get())->SetSize(BrakeValveSize, BrakeValveParams); - if (MBPM < 2) // jesli przystawka wazaca - Hamulec->SetLP(0, MaxBrakePress[3], 0); - else - Hamulec->SetLP(Mass, MBPM, MaxBrakePress[1]); - break; - } - case TBrakeValve::LSt: - { - WriteLog("XBT LSt"); - Hamulec = std::make_shared(MaxBrakePress[3], BrakeCylRadius, BrakeCylDist, BrakeVVolume, BrakeCylNo, BrakeDelays, BrakeMethod, NAxles, NBpA); - Hamulec->SetRM(RapidMult); - break; - } - case TBrakeValve::EStED: - { - WriteLog("XBT EStED"); - Hamulec = std::make_shared(MaxBrakePress[3], BrakeCylRadius, BrakeCylDist, BrakeVVolume, BrakeCylNo, BrakeDelays, BrakeMethod, NAxles, NBpA); - Hamulec->SetRM(RapidMult); - if (MBPM < 2) - { - // jesli przystawka wazaca - Hamulec->SetLP(0, MaxBrakePress[3], 0); - } - else - { - Hamulec->SetLP(Mass, MBPM, MaxBrakePress[1]); - } - break; - } - case TBrakeValve::EP2: - { - WriteLog("XBT EP2"); - Hamulec = std::make_shared(MaxBrakePress[3], BrakeCylRadius, BrakeCylDist, BrakeVVolume, BrakeCylNo, BrakeDelays, BrakeMethod, NAxles, NBpA); - Hamulec->SetLP(Mass, MBPM, MaxBrakePress[1]); - break; - } - case TBrakeValve::EP1: - { - WriteLog("XBT EP1"); - Hamulec = std::make_shared(MaxBrakePress[3], BrakeCylRadius, BrakeCylDist, BrakeVVolume, BrakeCylNo, BrakeDelays, BrakeMethod, NAxles, NBpA); - Hamulec->SetLP(Mass, MBPM, MaxBrakePress[1]); - Hamulec->SetRM(RapidMult); - break; - } - case TBrakeValve::CV1: - { - WriteLog("XBT CV1"); - Hamulec = std::make_shared(MaxBrakePress[3], BrakeCylRadius, BrakeCylDist, BrakeVVolume, BrakeCylNo, BrakeDelays, BrakeMethod, NAxles, NBpA); - break; - } - case TBrakeValve::CV1_L_TR: - { - WriteLog("XBT CV1_L_T"); - Hamulec = std::make_shared(MaxBrakePress[3], BrakeCylRadius, BrakeCylDist, BrakeVVolume, BrakeCylNo, BrakeDelays, BrakeMethod, NAxles, NBpA); - break; - } - default: - Hamulec = std::make_shared(MaxBrakePress[3], BrakeCylRadius, BrakeCylDist, BrakeVVolume, BrakeCylNo, BrakeDelays, BrakeMethod, NAxles, NBpA); + brake_valve_setup setup; + setup.valve = BrakeValve; + setup.max_brake_press = MaxBrakePress[3]; + setup.tare_max_brake_press = MaxBrakePress[1]; + setup.cylinder_radius = BrakeCylRadius; + setup.cylinder_stroke = BrakeCylDist; + setup.valve_volume = BrakeVVolume; + setup.cylinder_count = BrakeCylNo; + setup.delays = BrakeDelays; + setup.method = BrakeMethod; + setup.axles = NAxles; + setup.blocks_per_axle = NBpA; + setup.mass = Mass; + setup.load_brake_mass = MBPM; + setup.rapid_mult = RapidMult; + setup.valve_size = BrakeValveSize; + setup.valve_params = BrakeValveParams; + Hamulec = make_brake_valve(setup); } Hamulec->SetASBP(MaxBrakePress[4]); Hamulec->SetRV(RapidVel); - switch (BrakeHandle) - { - case TBrakeHandle::FV4a: - Handle = std::make_shared(); - break; - case TBrakeHandle::MHZ_EN57: - case TBrakeHandle::MHZ_K8P: - Handle = std::make_shared(); - break; - case TBrakeHandle::FVel6: - Handle = std::make_shared(); - break; - case TBrakeHandle::FVE408: - Handle = std::make_shared(); - break; - case TBrakeHandle::testH: - Handle = std::make_shared(); - break; - case TBrakeHandle::M394: - Handle = std::make_shared(); - break; - case TBrakeHandle::Knorr: - Handle = std::make_shared(); - break; - case TBrakeHandle::St113: - Handle = std::make_shared(); - break; - case TBrakeHandle::MHZ_K5P: - Handle = std::make_shared(); - break; - case TBrakeHandle::MHZ_6P: - Handle = std::make_shared(); - break; - default: - Handle = std::make_shared(); - } + Handle = make_driver_handle(BrakeHandle); Handle->SetParams(Handle_AutomaticOverload, Handle_ManualOverload, Handle_GenericDoubleParameter1, Handle_GenericDoubleParameter2, Handle_OverloadMaxPressure, Handle_OverloadPressureDecrease); - switch (BrakeLocHandle) - { - case TBrakeHandle::FD1: - { - LocHandle = std::make_shared(); - LocHandle->Init(MaxBrakePress[0]); - if (TrainType == dt_EZT) - { - - dynamic_cast(LocHandle.get())->SetSpeed(3.5); - } - break; - } - case TBrakeHandle::Knorr: - { - LocHandle = std::make_shared(); - LocHandle->Init(MaxBrakePress[0]); - break; - } - default: - LocHandle = std::make_shared(); - } + LocHandle = make_local_brake_handle(BrakeLocHandle, MaxBrakePress[0], TrainType == dt_EZT); if (true == TestFlag(BrakeDelays, bdelay_G) && (false == TestFlag(BrakeDelays, bdelay_R) || Power > 1.0)) // ustalanie srednicy przewodu glownego (lokomotywa lub napędowy Spg = 0.792; @@ -12830,6 +13002,12 @@ double TMoverParameters::ShowCurrentP(int AmpN) const bool TMoverParameters::reload_FIZ() { + if (Graph) + { + // the .vhg of a graph vehicle is reloaded by the preview server of the graph editor + WriteLog("[DEV] " + Name + " is defined by a vehicle graph, it has no .fiz to reload"); + return true; + } WriteLog("[DEV] Reloading FIZ for " + Name); // pause simulation Global.iPause |= 0b1000; diff --git a/McZapkie/Mover_shadow.cpp b/McZapkie/Mover_shadow.cpp new file mode 100644 index 00000000..47e684db --- /dev/null +++ b/McZapkie/Mover_shadow.cpp @@ -0,0 +1,161 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "McZapkie/MOVER.h" + +// VGRAPH_SHADOW_CHECK: the code of TMoverParameters extracted to the functions shared with the vehicle graph, in its +// original form, working on copies of the state. Mover runs it next to every call of the extracted functions and logs +// the differences, so the extraction can be verified on real vehicles. remove once the reference set of vehicles +// shows no differences +#ifdef VGRAPH_SHADOW_CHECK + +#include "utilities/Logs.h" +#include "utilities/utilities.h" + +namespace +{ + +// as in Mover.cpp +inline double square(double val) +{ + return val * val; +} + +} // namespace + +void TMoverParameters::shadow_compare(char const *What, double const Original, double const Extracted) const +{ + if (Original == Extracted || (std::isnan(Original) && std::isnan(Extracted))) + { + return; + } + WriteLog("VGRAPH_SHADOW_CHECK: " + Name + ": " + What + " differs: original " + std::to_string(Original) + ", extracted " + std::to_string(Extracted)); +} + +double TMoverParameters::shadow_series_motor_current(double const U, double const n, double const Mn, double const Rz, int const SP) const +{ + double MotorCurrent; + double U1 = U + Mn * n * motor_param(SP).fi0 * motor_param(SP).fi; + double Isf = Sign(U1) * motor_param(SP).Isat; + double Delta = square(Isf * Rz + Mn * motor_param(SP).fi * n - U1) + 4.0 * U1 * Isf * Rz; + if (U > 0) + MotorCurrent = (U1 - Isf * Rz - Mn * motor_param(SP).fi * n + std::sqrt(Delta)) / (2.0 * Rz); + else + MotorCurrent = (U1 - Isf * Rz - Mn * motor_param(SP).fi * n - std::sqrt(Delta)) / (2.0 * Rz); + return MotorCurrent; +} + +double TMoverParameters::shadow_brake_rigging_shoe_force(double K, double &Ntotal) const +{ + double u = (BrakePress * P2FTrans - BrakeCylSpring) * BrakeCylMult[0] - BrakeSlckAdj; + if (u * BrakeRigEff > Ntotal) // histereza na nacisku klockow + Ntotal = u * BrakeRigEff; + else + { + u = (BrakePress * P2FTrans - BrakeCylSpring) * BrakeCylMult[0] - BrakeSlckAdj; + if (u * (2.0 - BrakeRigEff) < Ntotal) // histereza na nacisku klockow + Ntotal = u * (2.0 - BrakeRigEff); + } + + auto const NBrakeAxles{NAxles}; + + if (NBrakeAxles * NBpA > 0) + { + if (Ntotal > 0) // nie luz + K += Ntotal; // w kN + K *= static_cast(BrakeCylNo) / (NBrakeAxles * static_cast(NBpA)); // w kN na os + } + return K; +} + +void TMoverParameters::shadow_induction_motor(double const PosRatio, double tmp, double const dt, double *Eimv, double &Eimvpr, double &Vaddcopy, induction_motor_outputs &Output) const +{ + Eimvpr += std::max(std::min(PosRatio - Eimvpr, 0.02), -0.02) * 12 * tmp /*2{+4*byte(PosRatio= 0) + Vaddcopy *= 1.0 - 2.0 * dt; + else if (std::abs(EngineVoltage) < EnginePowerSource.CollectorParameters.MaxV) + Vaddcopy *= 1.0 - dt; + else + Vaddcopy = std::max(Vaddcopy * (1.0 - 0.2 * dt), 0.007 * (std::abs(EngineVoltage) - (EnginePowerSource.CollectorParameters.MaxV - 100))); + Output.Itot = Eimv[eimv_Ipoj] * (0.01 + std::min(0.99, 0.99 - Vaddcopy)); + + Output.EnginePower = abs(Eimv[eimv_Ic] * Eimv[eimv_U] * NPoweredAxles) / 1000; + Output.Mm = Eimv[eimv_M] * DirAbsolute; + Output.Mw = Output.Mm * Transmision.Ratio * Transmision.Efficiency; + Output.Fw = Output.Mw * 2.0 / WheelDiameter; + Output.Ft = Output.Fw * NPoweredAxles * InvertersRatio; + Eimv[eimv_Fr] = DirAbsolute * Output.Ft / 1000; +} + +#endif diff --git a/McZapkie/hamulce.cpp b/McZapkie/hamulce.cpp index 64a7fb85..5aeca74a 100644 --- a/McZapkie/hamulce.cpp +++ b/McZapkie/hamulce.cpp @@ -342,6 +342,45 @@ begin end ; */ +// friction material of the brake shoes or discs; shared with the vehicle graph +std::shared_ptr make_friction_material(int const Material) +{ + std::shared_ptr result; + switch (Material) + { + case bp_P10Bg: + result = std::make_shared(); + break; + case bp_P10Bgu: + result = std::make_shared(); + break; + case bp_FR513: + result = std::make_shared(); + break; + case bp_FR510: + result = std::make_shared(); + break; + case bp_Cosid: + result = std::make_shared(); + break; + case bp_P10yBg: + result = std::make_shared(); + break; + case bp_P10yBgu: + result = std::make_shared(); + break; + case bp_D1: + result = std::make_shared(); + break; + case bp_D2: + result = std::make_shared(); + break; + default: // domyslnie + result = std::make_shared(); + } + return result; +} + /// /// Constructs the brake unit: stores parameters, creates the cylinder / /// auxiliary reservoir / valve pre-chamber, sizes them for the requested @@ -384,39 +423,7 @@ TBrake::TBrake(double i_mbp, double i_bcr, double i_bcd, double i_brc, int i_bcn ValveRes->CreateCap(0.25); // materialy cierne - i_mat = i_mat & 255 - bp_MHS; - switch (i_mat) - { - case bp_P10Bg: - FM = std::make_shared(); - break; - case bp_P10Bgu: - FM = std::make_shared(); - break; - case bp_FR513: - FM = std::make_shared(); - break; - case bp_FR510: - FM = std::make_shared(); - break; - case bp_Cosid: - FM = std::make_shared(); - break; - case bp_P10yBg: - FM = std::make_shared(); - break; - case bp_P10yBgu: - FM = std::make_shared(); - break; - case bp_D1: - FM = std::make_shared(); - break; - case bp_D2: - FM = std::make_shared(); - break; - default: // domyslnie - FM = std::make_shared(); - } + FM = make_friction_material(i_mat & 255 - bp_MHS); } /// diff --git a/McZapkie/hamulce.h b/McZapkie/hamulce.h index a05a3635..69681cb7 100644 --- a/McZapkie/hamulce.h +++ b/McZapkie/hamulce.h @@ -1623,6 +1623,10 @@ class TFVE408 : public TDriverHandle /// Soft-clip pressure delta — softens flow for tiny PH-PL differences (default 0.25). /// Volumetric flow rate (signed; positive = P1->P2 direction). extern double PF(double const P1, double const P2, double const S, double const DP = 0.25); + +// friction material of the brake shoes or discs, one of the bp_* constants without bp_MHS +std::shared_ptr make_friction_material(int Material); + /// /// Variant of that uses the dimensionless pressure ratio (sg) /// for the soft-clip threshold instead of an absolute pressure delta. diff --git a/application/application.cpp b/application/application.cpp index a7d09814..d8b2d5b0 100644 --- a/application/application.cpp +++ b/application/application.cpp @@ -1168,6 +1168,15 @@ int eu07_application::init_settings(int Argc, char *Argv[]) else editwithoutscenery = true; } + else if (token == "-graph-preview") + { + // live preview server of the vehicle graph editor, on the default or the specified port + Global.vehiclegraph_preview = true; + if (i + 1 < Argc && Argv[i + 1][0] >= '0' && Argv[i + 1][0] <= '9') + { + Global.vehiclegraph_preview_port = std::atoi(Argv[++i]); + } + } else if (token == "-seed") { // a number is the seed; anything else is a word that stands for one. said in the @@ -1183,7 +1192,8 @@ int eu07_application::init_settings(int Argc, char *Argv[]) std::cout << "usage: " << std::string(Argv[0]) << " [-s sceneryfilepath]" << " [-v vehiclename]" << " [-edit [sceneryfilepath]]" - << " [-seed number|word]" << std::endl; + << " [-seed number|word]" + << " [-graph-preview [port]]" << std::endl; return -1; } } diff --git a/application/drivermode.cpp b/application/drivermode.cpp index b55d09c2..b753e33c 100644 --- a/application/drivermode.cpp +++ b/application/drivermode.cpp @@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "application/drivermode.h" #include "application/driveruilayer.h" +#include "vehicle/graph/graph_preview.h" #include "utilities/Globals.h" #include "application/application.h" @@ -170,6 +171,8 @@ bool driver_mode::update() simulation::State.update_clocks(); simulation::State.update_scripting_interface(); simulation::Environment.update(); + // live preview connections of the vehicle graph editor + vgraph::preview_update(); if (deltatime != 0.0 || false == simulation::is_ready) { diff --git a/application/driveruilayer.cpp b/application/driveruilayer.cpp index 569ade33..198c0792 100644 --- a/application/driveruilayer.cpp +++ b/application/driveruilayer.cpp @@ -37,6 +37,7 @@ driver_ui::driver_ui() add_external_panel(&m_logpanel); add_external_panel(&m_perfgraphpanel); add_external_panel(&m_cameraviewpanel); + add_external_panel(&m_vehiclegraphpanel); m_logpanel.is_open = false; m_aidpanel.title = STR("Driving Aid"); @@ -81,6 +82,7 @@ void driver_ui::render_menu_contents() ImGui::MenuItem(m_vehiclelist.name().c_str(), nullptr, &m_vehiclelist.is_open); ImGui::MenuItem(m_trainingcardpanel.name().c_str(), nullptr, &m_trainingcardpanel.is_open); ImGui::MenuItem(m_cameraviewpanel.name().c_str(), nullptr, &m_cameraviewpanel.is_open); + ImGui::MenuItem(m_vehiclegraphpanel.name().c_str(), nullptr, &m_vehiclegraphpanel.is_open); if (DebugModeFlag) ImGui::MenuItem(m_perfgraphpanel.name().c_str(), nullptr, &m_perfgraphpanel.is_open); diff --git a/application/driveruilayer.h b/application/driveruilayer.h index 8ad1d775..d77dc5c7 100644 --- a/application/driveruilayer.h +++ b/application/driveruilayer.h @@ -19,6 +19,7 @@ http://mozilla.org/MPL/2.0/. #include "widgets/time.h" #include "widgets/trainingcard.h" #include "widgets/perfgraphs.h" +#include "widgets/vehiclegraph.h" #include "widgets/cameraview_extcam.h" class driver_ui : public ui_layer { @@ -69,4 +70,5 @@ private: ui::map_panel m_mappanel; ui::time_panel m_timepanel; ui::cameraview_panel m_cameraviewpanel; + ui::vehiclegraph_panel m_vehiclegraphpanel; }; diff --git a/input/command.cpp b/input/command.cpp index 9a373fef..1da1aac0 100644 --- a/input/command.cpp +++ b/input/command.cpp @@ -405,6 +405,7 @@ commanddescription_sequence Commands_descriptions = { {"wiperswitchincrease", command_target::vehicle, command_mode::oneoff}, {"wiperswitchdecrease", command_target::vehicle, command_mode::oneoff}, {"lightsset", command_target::vehicle, command_mode::oneoff}, + {"vehiclegraphcontrol", command_target::vehicle, command_mode::oneoff}, }; // Maps of command and coresponding strings @@ -789,6 +790,7 @@ std::unordered_map commandMap = { {"wiperswitchincrease", user_command::wiperswitchincrease}, {"wiperswitchdecrease", user_command::wiperswitchdecrease}, {"lightsset", user_command::lightsset}, + {"vehiclegraphcontrol", user_command::vehiclegraphcontrol}, {"none", user_command::none}}; } // namespace simulation diff --git a/input/command.h b/input/command.h index 7ff5b272..6fea3664 100644 --- a/input/command.h +++ b/input/command.h @@ -403,6 +403,9 @@ enum class user_command wiperswitchincrease, wiperswitchdecrease, lightsset, + // operates a control of a vehicle defined by a graph; the payload names the control (the id of its cab.switch block), + // param1 is the direction: positive for the left mouse button or increase, negative for the right one or decrease + vehiclegraphcontrol, none = -1 }; diff --git a/input/drivermouseinput.cpp b/input/drivermouseinput.cpp index 2d5f3d24..14530e07 100644 --- a/input/drivermouseinput.cpp +++ b/input/drivermouseinput.cpp @@ -338,7 +338,7 @@ drivermouse_input::button( int const Button, int const Action ) { // NOTE: basic keyboard controls don't have any parameters // as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 // TODO: pass correct entity id once the missing systems are in place - m_relay.post( mousecommand, 0, 0, Action, 0 ); + post( mousecommand, Action ); mousecommand = user_command::none; } else { @@ -373,9 +373,14 @@ drivermouse_input::button( int const Button, int const Action ) { control->screen_touch_list->emplace_back(pos); } - auto const controlbindings { bindings( simulation::Train->GetLabel( control ) ) }; + auto const label { simulation::Train->GetLabel( control ) }; + auto const controlbindings { bindings( label ) }; // if the recognized element under the cursor has a command associated with the pressed button, notify the recipient mousecommand = Button == GLFW_MOUSE_BUTTON_LEFT ? controlbindings.first : controlbindings.second; + if( mousecommand == user_command::vehiclegraphcontrol ) { + m_graphcontrol = label.substr( 6 ); + m_graphdirection = ( Button == GLFW_MOUSE_BUTTON_LEFT ? 1.0 : -1.0 ); + } if( mousecommand == user_command::none ) { // if we don't have any recognized element under the cursor and the right button was pressed, enter view panning mode @@ -428,9 +433,9 @@ drivermouse_input::button( int const Button, int const Action ) { // NOTE: basic keyboard controls don't have any parameters // NOTE: as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 // TODO: pass correct entity id once the missing systems are in place - m_relay.post( mousecommand, 0, 0, Action, 0 ); + post( mousecommand, Action ); if (!pickwaiting) // already depressed - m_relay.post( mousecommand, 0, 0, GLFW_RELEASE, 0 ); + post( mousecommand, GLFW_RELEASE ); m_updateaccumulator = -0.25; // prevent potential command repeat right after issuing one } ); } @@ -459,13 +464,13 @@ drivermouse_input::poll() { // NOTE: basic keyboard controls don't have any parameters // as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 // TODO: pass correct entity id once the missing systems are in place - m_relay.post( m_mousecommandleft, 0, 0, GLFW_REPEAT, 0 ); + post( m_mousecommandleft, GLFW_REPEAT ); } if( m_mousecommandright != user_command::none ) { // NOTE: basic keyboard controls don't have any parameters // as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 // TODO: pass correct entity id once the missing systems are in place - m_relay.post( m_mousecommandright, 0, 0, GLFW_REPEAT, 0 ); + post( m_mousecommandright, GLFW_REPEAT ); } m_updateaccumulator -= updaterate; } @@ -477,10 +482,28 @@ drivermouse_input::command() const { return m_slider.command() != user_command::none ? m_slider.command() : m_mousecommandleft != user_command::none ? m_mousecommandleft : m_mousecommandright; } +void +drivermouse_input::post( user_command const Command, int const Action ) { + + if( Command == user_command::vehiclegraphcontrol ) { + m_relay.post( Command, m_graphdirection, 0, Action, 0, glm::vec3( 0.0f ), &m_graphcontrol ); + } + else { + // NOTE: basic keyboard controls don't have any parameters + // as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 + // TODO: pass correct entity id once the missing systems are in place + m_relay.post( Command, 0, 0, Action, 0 ); + } +} + // returns pair of bindings associated with specified cab control std::pair drivermouse_input::bindings( std::string const &Control ) const { + if( Control.starts_with( "graph:" ) ) { + // controls of vehicle graphs, operated with both buttons + return { user_command::vehiclegraphcontrol, user_command::vehiclegraphcontrol }; + } auto const lookup{ m_buttonbindings.find( Control ) }; if( lookup != m_buttonbindings.end() ) diff --git a/input/drivermouseinput.h b/input/drivermouseinput.h index 142ef99c..ce5cc087 100644 --- a/input/drivermouseinput.h +++ b/input/drivermouseinput.h @@ -92,6 +92,9 @@ private: // potentially replaces supplied command with a more relevant one user_command adjust_command( user_command Command ); + // posts the command; commands of vehicle graph controls carry the name of the control + void + post( user_command const Command, int const Action ); // members command_relay m_relay; @@ -109,6 +112,9 @@ private: glm::dvec2 m_varyingpollrateorigin; // helper, cursor position when the command was initiated std::array m_buttons; bool m_pickwaiting; + // control of a vehicle graph under the cursor (label "graph:"), and the direction given by the mouse button + std::string m_graphcontrol; + double m_graphdirection { 0.0 }; }; //--------------------------------------------------------------------------- \ No newline at end of file diff --git a/tools/vehicle_graph/README.md b/tools/vehicle_graph/README.md index 06ae25a6..556a1c96 100644 --- a/tools/vehicle_graph/README.md +++ b/tools/vehicle_graph/README.md @@ -1,11 +1,16 @@ # Vehicle graph (.vhg) -A vehicle graph describes a vehicle as connected blocks, in place of the `.fiz` file. The graph holds the cab -controls and indicators, the logic, the sounds and, in the following stages, the electrical, mechanical and -pneumatic systems of the vehicle. The simulator code is in `vehicle/graph/`. +A vehicle graph describes a vehicle as connected blocks, in place of the `.fiz` file: the cab controls and +indicators, the logic, the sounds, and the electrical, mechanical and pneumatic systems of the vehicle. The simulator +code is in `vehicle/graph/`; the external editor (`maszyna-graph-editor`) reads the block catalog exported by the +simulator and runs the same blocks in its preview. -Current stage: the core of the graph with logic and cab blocks, available through the command line tools below. -Vehicles aren't loaded from `.vhg` files yet. +A vehicle is a graph vehicle when `.vhg` lies next to its `.mmd`. Then the `.fiz` isn't read, and the +`.mmd` gives only the models: the sounds of the vehicle and the controls and indicators of its cabs come from the graph. +The only exception is the body: entries of the body the graph doesn't give (e.g. a graph made only of cab logic, without +the `body` section) are taken from the body sections (`Param.`, `Load:`, `Dimensions:`, `Wheels:`, `BuffCoupl.`, +`Headlights:`) of `.fiz`, if there is one. A graph vehicle without length or mass isn't loaded, with a message in the +log. ## File format, version 2 @@ -17,11 +22,11 @@ A `.vhg` file is JSON. Comments (`//`, `/* */`) are allowed. "version": 2, "vehicle": { "name": "ET22" }, "settings": { "low_voltage_simulation": false }, - "body": { }, + "body": { "mass": 83500, "length": 16.315, "wheel_diameter": 1.25, "axle_arrangement": "Bo'Bo'" }, "profiles": { "brake_pipe": 1 }, "nodes": [ - { "id": "bat", "type": "cab.switch", "title": "Battery", "params": { "behaviour": "bistable", "submodel": "battery_sw:" } }, - { "id": "lamp", "type": "fb.lamp", "params": { "color": "red" } } + { "id": "bat", "type": "cab.switch", "title": "Battery", "params": { "behaviour": "bistable", "submodel": "battery_sw", "command": "batterytoggle" } }, + { "id": "lamp", "type": "fb.lamp", "params": { "color": "red", "submodel": "i-battery" } } ], "connections": [ { "from": "bat.pos", "to": "lamp.on" } @@ -31,17 +36,53 @@ A `.vhg` file is JSON. Comments (`//`, `/* */`) are allowed. ``` - `nodes[].id`: letters, digits, `_` and `-`; unique in the file. -- `nodes[].type`: block type from the catalog (see `-graph-catalog`). +- `nodes[].type`: block type from the catalog (see `-graph-catalog` and the list below). - `nodes[].params`: typed values. Numbers are JSON numbers, switches are `true`/`false`, choices are ASCII ids - from the catalog (e.g. `"bistable"`, `"rotate"`, `"ne"`). Parameters left out take their default values. + from the catalog (e.g. `"bistable"`, `"rotate"`, `"ne"`), tables are arrays of rows of numbers + (`"rlist": [[0, 0, 0, 0, 0], [1, 24.891, 1, 4, 0]]`; the catalog names the columns). Parameters left out take their + default values. - `connections`: `"node.port"` of an output to `"node.port"` of an input. An output can feed many inputs; an input has at most one source. Unconnected inputs see their default value. - `settings.low_voltage_simulation`: when enabled, blocks with low voltage ports simulate the voltages and currents of the control circuits (e.g. a lamp needs enough voltage and draws current). When disabled, low voltage connections have no effect and the apparatus behaves as if always powered. -- `body`, `profiles`: parameters of the vehicle body and standard interfaces between vehicles; used by the next stages. +- `body`: the vehicle body, see below. +- `profiles`: standard interfaces the vehicle uses, with their versions (`brake_pipe`, `feed_pipe`, `heating`, `ai`, + all version 1). Unknown profiles are reported; newer versions than the simulator knows are rejected. - `layout`: positions of the blocks in the editor; ignored by the simulator. +### Body + +The body stays native in the simulator: mass, dimensions, axles, couplers, motion along the track, collisions. Its +entries replace the `.fiz` entries named in the catalog (`"fiz"` of each body entry); the simulator writes them as +`.fiz` lines and reads them with the same code as the `.fiz` files, so defaults and derived values are the same. +Entries left out keep the defaults of the `.fiz` reader. + +| key | unit | .fiz | key | unit | .fiz | +|---|---|---|---|---|---| +| category (train, road, unimog, ship) | | Param. Category | coupler_type | | BuffCoupl. CType | +| mass | kg | Param. M | coupler_kb, coupler_kc | kN/m | BuffCoupl. kB, kC | +| mass_rotating | kg | Param. Mred | coupler_dmax_b, coupler_dmax_c | m | BuffCoupl. DmaxB, DmaxC | +| vmax | km/h | Param. Vmax | coupler_fmax_b, coupler_fmax_c | kN | BuffCoupl. FmaxB, FmaxC | +| power | kW | Param. PWR | coupler_beta | | BuffCoupl. beta | +| sand_capacity | kg | Param. SandCap | coupler_allowed_flag | | BuffCoupl. AllowedFlag | +| heating_power | kW | Param. HeatingP | control_type | | BuffCoupl. ControlType | +| length, height, width | m | Dimensions: L H W | max_load | | Load: MaxLoad | +| cx | | Dimensions: Cx | load_unit | | Load: LoadQ | +| wheel_diameter | m | Wheels: D | load_accepted | | Load: LoadAccepted | +| wheel_inertia | kg m2 | Wheels: AIM | load_speed, unload_speed | 1/s | Load: LoadSpeed UnLoadSpeed | +| track_gauge | m | Wheels: Tw | light_color ("r g b") | | Headlights: LampRed/Green/Blue | +| axle_arrangement | | Wheels: Axle | collector_type | | Power: PantType | +| axle_distance, bogie_distance | m | Wheels: Ad Bd | collector_min_h, collector_max_h | m | Power: MinH MaxH | +| min_radius | m | Wheels: Rmin | collector_csw | m | Power: CSW | +| bearing (roll, slide) | | Wheels: BearingType | collector_max_voltage, collector_min_voltage | V | Power: MaxVoltage MinV | +| | | | collector_max_current | A | Power: MaxCurrent | +| | | | collector_layout | | Power: PhysicalLayout | + +The equipment blocks which stand for parts of the `.fiz` vehicle (brakes, compressor, security system, doors...) +mark their parameters with the `.fiz` entries they replace (`"fiz"` of the parameter in the catalog), and are +written to the body the same way. The pantographs need `power` above zero. + ### Ports and domains Signal ports carry one value (bool, int or float; booleans are 0 and 1). Physical ports carry a pair of values @@ -56,24 +97,245 @@ in opposite directions: | `traction` | force at the wheel rim [N] | velocity [m/s] | | `air`, `brake_pipe`, `feed_pipe` | pressure [bar] | flow | +Mechanical values are in the frame of the vehicle: positive torque pushes the vehicle towards its front (cab 1), +positive speed is the rotation when moving that way. The `dir` input of the motors is the position of the reverser +in the occupied cab; the motors turn it to the frame of the vehicle. + +The air domains connect blocks of the vehicle's own air system: the brake pipe and the feed pipe of the body, +its main reservoir, the brake valves created for the body. The link values show the pressures; the flows go +between the reservoirs of the body, the same way as for the `.fiz` vehicles. + ### Order of evaluation -Blocks are evaluated once per step in the order given by the connections. A loop of connections has to pass through -a deferred block (`logic.sr`, `logic.ton`, `logic.tof`, `logic.delay`), whose outputs change in the next step; other -loops are rejected when the file is loaded. Back values of physical connections are always taken from the previous -step. The graph has no other source of time or randomness, so the same inputs give the same results. +Blocks are evaluated once per physics step, in the order given by the connections. A loop of connections has to pass +through a deferred block (`logic.sr`, `logic.ton`, `logic.tof`, `logic.delay`, `pn.governor`, `el.hsb`, +`el.contactor`), whose outputs change in the next step; other loops are rejected when the file is loaded. Back values +of physical connections are always taken from the previous step. Some blocks (`br.pipe`, `pn.reservoir`) also act +after all blocks were evaluated, e.g. to exchange air with the neighbours. Blocks marked `single` in the catalog stand +for parts the vehicle has one of (brake pipe, main reservoir, distributor, line breaker...): a graph can have one each. +The graph has no other source of time or randomness, so the same inputs give the same results. + +## Blocks + +`-graph-catalog` gives the full list with all ports, parameters, units and defaults. Origin: *simulator* blocks drive +the existing classes unchanged; *extracted* blocks use code moved out of `TMoverParameters` into functions shared with +the `.fiz` vehicles; *native* blocks are parts of the vehicle body. + +**Logic** (`logic.*`): `compare`, `and`, `or`, `xor`, `nand`, `nor`, `not`, `sr`, `ton`, `tof`, `delay`, `hyst`, `const`, +`math`, `select`. **Script**: `lua.block` (below). + +**Cab** (all with `submodel` and `cab`: `any`, `1`, `2` - the cab whose model holds the submodel, `0` - the engine room, +as `cab0definition:` of the `.mmd`): +- `cab.switch` - switch, button or lever: `behaviour` (bistable, monostable, multi), positions, min/max values, default, + `spring`, animation (rotate in degrees, move in millimetres per position, `offset`, `smoothing`), `sound_inc` and + `sound_dec` (played at the submodel when the position goes up or down; the same format as `soundinc:` and + `sounddec:` of the `.mmd` controls: a file, a random set `[a.wav b.wav]` or a `{ }` block), `sound_positions` + (sounds of single positions as the `soundN:` entries of the `.mmd` controls, with N the number of the position from + 0, e.g. `sound0: a.wav sound1: b.wav`; a position with its own sound plays it in place of `sound_inc`/`sound_dec`). Outputs `pos` + (value) and `index` (number of the position). `command`: a simulator command which presses it (e.g. `batterytoggle`; + pressed and released with the key); `key_inc`/`key_dec`: commands which move it up and down (e.g. + `mastercontrollerincrease`), so the keyboard bindings of the simulator operate the graph. `hw_name`: name for the + hardware desk (not used yet). +- `fb.lamp` - indicator lamp (`_on`/`_off` as for the `.mmd` lamps; `sound_on`/`sound_off` played when it + lights and goes out, as `soundinc:`/`sounddec:` of the `.mmd` lamps), `fb.gauge`, `fb.manometer` (two needles), + `fb.sound` (file, volume, pitch, loop, placement as `sound_placement`, range, offset), `fb.export` (value for the + python screens of the cab, under `name`). + +**Vehicle** (native): `veh.state` (speed, acceleration, wheel revolutions, slip, occupied cab, time), `veh.doors` +(open left/right, lock; the doors of the body move them), `veh.lights` (light bit masks of the front and rear ends, +dimming), `veh.sander`, `veh.anim` and `veh.visible` (submodels of the external model of the vehicle, `model`: +`vehicle` or `lowpoly` for the low poly interior: `veh.anim` rotates (degrees about y) or moves (millimetres along z) +`submodel` by `offset + scale * value` with the smoothing of TGauge; `veh.visible` shows `submodel` when on and +`submodel_off` when off, optionally blinking. They're applied per vehicle right before it's drawn, so vehicles sharing +a model show their own state; `-graph-run` computes only the state of the blocks). + +**Electrical**: `el.pantograph` (index 0 front, 1 rear pantograph of the vehicle model; raising needs air when +connected; without the simulator the catenary has `preview_voltage`), `el.junction`, `el.hsb` (line breaker: Mains of +the body; closing time, init time, undervoltage, overcurrent trip), `el.contactor`, `el.converter` (ConverterFlag), +`el.battery`, `el.voltage_relay`, `el.transformer` (below). + +Several supply systems: `el.hsb` and `el.converter` may appear several times. The body's Mains is closed when any line +breaker is closed, ConverterFlag/ConverterAllow are set when any converter runs / is switched on. +- `el.voltage_relay` (in: high_voltage, draws nothing; out: `ok`, `u` [V]; params `min_voltage`, `max_voltage` (0 = no + upper limit), `delay` [s]): `ok` when the voltage stayed within the range for the delay. Use it to recognise the + supply system and to close or hold the breaker of that system. +- `el.transformer` (in/out: high_voltage; params `ratio` = out/in, `eff`): out voltage = in × ratio, input current = + output current × ratio / eff. Main transformer of AC vehicles or AC branches (rectifier implied). +- The simulator gives the vehicles only the catenary voltage (no AC/DC or frequency information: the frequency of the + power source in the scenery isn't passed to the vehicles), so the systems are told apart by voltage. +- `el.converter`: output `aux` (aux_ac, `out_voltage`) and optional `nn` (low_voltage, `nn_voltage`; 0 = off), whose + power (current × voltage) is drawn from the high voltage as well. `aux_kind` (ac3 | ac1 | dc) only labels the + auxiliary circuit for the editor. + +**Drive**: `drive.dc_series` (series DC motors with resistor starting, the `.fiz` RList and MotorParamTable0 as tables; +the motor equations of `TMoverParameters::Current` and `Momentum`), `drive.async` (induction motors with inverters, the +`Engine:` parameters of `ElectricInductionMotor`; the motor and inverter computation of `TractionForce`; demand -1..1, +negative for the electrodynamic brake; `ed` output for the blending with the distributor), `drive.simple` (motor with +torque and power limits, for prototypes), `drive.gear`, `drive.axle` (the sum of all axle forces is the tractive force +of the vehicle). + +Combustion engines: `drive.diesel` (starter and start time, all-speed or min/max governor, torque from a table rpm → Nm +or kW, or the Mover parabola Mmax/nMmax/Mnmax, friction Mstand, fuel consumption; `dizel_Momentum`), `drive.gearbox` +(friction clutch and gearbox with a table of gears, shifting with the clutch released), `drive.hydro` (torque converter +with filling, fluid coupling range and lockup clutch, the `hydro_TC` entries of the `.fiz`), `drive.generator` (main +generator of diesel-electric vehicles: excitation, voltage ellipse, power limit scaled with the engine speed; feeds +`drive.dc_series`). The engine shaft goes to exactly one of the last three: that block holds the engine speed and its +inertia (`.fiz` AIM / 2π) and returns it on the back channel; a locked clutch makes the engine follow the wheels. +`drive.diesel` keeps `Mains` closed while the engine runs and, with `main_engine`, sets `enrot`, `dizel_fill`, `Mm`, +`EnginePower`; the gearbox and the hydraulic transmission set `dizel_engage` and `hydro_TC_*`. Graph vehicles have no +`EngineType`, so the engine sounds use the generic formulas of the vehicle sounds. + +**Pneumatics and brakes**: `pn.compressor`, `pn.governor`, `pn.reservoir` (main reservoir and feed pipe), `br.handle` +(driver's brake valve; `type` as `BrakeHandle` of the `.fiz`; `pos` in the valve's own scale, e.g. -2..6 for FV4a, +-1..10 for MHZ_K8P), `br.local_handle` (FD1, Knorr), `br.pipe` (brake pipe; `vent` is the emergency valve), +`br.distributor` (`type` as `BrakeValve`, `mode` as `BrakeDelays` of the `.fiz`), `br.rigging` (brake force from the +cylinder pressure; the sum is the brake force of the vehicle), `pn.spring_brake`. + +**Security**: `sec.ca` (TSecuritySystem of the body: vigilance, cab signalling with the magnets of the track, radio stop; +`brake` for the `vent` of the brake pipe). + +**Interfaces**: `if.pg`, `if.pz` (end cocks of the brake pipe and the feed pipe at one end; the exchange with the +neighbour is open only when both cocks of the coupling are open; the `.fiz` vehicles have them always open), +`if.heat` (train heating through the couplers, from the main circuit), `if.ai` (intents of the AI driver; not used yet), +`if.line` (below). + +Multiple unit control, `if.line`: a named control line shared by the coupled graph vehicles, like a train wire (params +`name`, `combine` max|min|sum|maxabs|or, `reverse`, `pair`, `coupling` +control|permanent|coupler|highvoltage|heating|power110v|power24v|power3x400v). A vehicle drives the line when `in` is +connected and `active` (default 1) is set; `out` combines the values of all driving vehicles in this vehicle's +orientation (`reverse`: the value of a vehicle facing the other way changes sign; `pair`: such a vehicle gives its pair +line, e.g. drzwi_lewe ↔ drzwi_prawe), `vehicles` counts the vehicles on the line, `drivers` the driving ones. The line +ends at a coupling without the flag, a vehicle without a graph, or a vehicle without the line. Values of the other +vehicles arrive with up to one step of delay. Multiple unit control between graph and `.fiz` vehicles isn't supported. + +## Graph vehicles in the simulator + +- The body (`TMoverParameters`) is filled from the `body` section and the equipment parameters, then + `CheckLocomotiveParameters` runs as for a `.fiz` vehicle: the brake valves (`Hamulec`, `Handle`, `LocHandle`) are + created by the same factories, from the parameters of `br.distributor`, `br.handle` and `br.local_handle`, and the + blocks drive them. The initial state follows the scenario (standing vehicles braked, moving ones ready). +- In every physics step the graph runs in place of the subsystems of `compute_movement_`. The tractive force is the sum + of the `drive.axle` blocks, the brake force the sum of the `br.rigging` blocks; slip, couplers, motion resistance and + gravity stay native. The brake pipe and the feed pipe exchange air with the neighbours as before, so graph vehicles + run in trains with `.fiz` wagons; without `br.pipe` or `pn.reservoir` the pipes of the body just pass the air on. +- The fields other parts of the simulator read are kept by the blocks: `Mains` (`el.hsb`, `drive.diesel`), `ConverterFlag`, + `CompressorFlag`, `Pantographs[].is_active`, `PantPress`, `Im`, `EngineVoltage`, `enrot` (drives), `Battery`, + `PipePress`, `ScndPipePress`, `BrakePress`, `Compressor`, `iLights`, the doors. +- The cab: the controls and indicators of the `.mmd` aren't used; the cab blocks of the graph are created in the + cab model when the cab is entered, with labels `graph:` (both mouse buttons operate them through the + `vehiclegraphcontrol` command, whose payload is the id). Commands bound with `command`, `key_inc`, `key_dec` reach + the graph before the simulator's own handlers. +- Sounds: the `sounds:` section of the `.mmd` and the cab sounds aren't read; `fb.sound` blocks and the sounds of the + cab switches and lamps play instead. +- The AI doesn't drive graph vehicles yet: a graph vehicle left to the AI stays as its graph keeps it, with a warning in + the log. Graph vehicles control each other through `if.line`; multiple unit control between graph and `.fiz` + vehicles isn't supported. +- Messages of the graph (lua `print`, script errors) and the problems of the file go to the log. +- The inspector: window "Vehicle graph" (menu Mode windows) shows the blocks of the occupied vehicle with the values of + their ports and their states, and forces the signal outputs. +- `fb.export` values appear in the state passed to the python screens of the cab. + +## Live preview server + +Off unless enabled with `-graph-preview [port]` on the command line, or `vehiclegraph_preview yes` (and optionally +`vehiclegraph_preview_port 7420`) in `eu07.ini`. The simulator listens on `127.0.0.1`, port 7420 by default, for +connections of the editor. One JSON object per line (UTF-8, `\n`), protocol version 1: + +| client | simulator | +|---|---| +| `{"op":"hello","version":1}` | `{"op":"hello","version":1,"simulator":"MaSzyna "}` | +| `{"op":"list"}` | `{"op":"vehicles","items":[{"name":"ET22-1001","file":"dynamic/pkp/et22/303e.vhg","graph":"303E"}]}` | +| `{"op":"attach","vehicle":"ET22-1001"}` | `{"op":"attached","vehicle":"...","file":"..."}`, then 10 times a second `{"op":"values","time":12.34,"signals":{"node.port":v},"states":{"node:state":v},"links":{"node.port":[fwd,back]}}` | +| `{"op":"detach"}` | | +| `{"op":"switch","node":"id","action":"press\|release\|toggle\|inc\|dec\|set\|value","value":0}` | | +| `{"op":"force","port":"node.port","value":1.0}`, `{"op":"unforce","port":"node.port"}` | | +| `{"op":"reload"}` | `{"op":"reloaded"}` or `{"op":"error","message":"..."}` with the problems of the file | +| `{"op":"submodels"}` | `{"op":"submodels","vehicle":{"model":"...","submodels":[...]},"lowpoly":{...} or null,"cabs":[{"cab":0,"model":"...","submodels":[...]}]}` | + +`submodels` (attached only) gives the sorted names of the submodels of the vehicle model, the low poly interior and the +cab models listed in the `.mmd` (`cabNmodel:`; `cab1model: none` gives the vehicle model), for the pickers of the +editor. Cab models not loaded yet are loaded through the models manager, as when switching cabs; in a network game +only the player's own train lists its cabs. Older simulators answer with an `unknown op` error. + +Errors are answered with `{"op":"error","message":"..."}`; the connection stays open. `reload` reads the attached +vehicle's `.vhg` again: the body keeps its state, the blocks start anew. The server runs on the simulation thread, +polled once per frame. + +## Lua blocks + +A `lua.block` node holds a lua 5.1 script in `"lua"`. The script defines the block's ports, parameters and behaviour: + +```lua +block "power_brake" { + inputs = { lever = "float", ready = "bool" }, -- bool, int or float + outputs = { traction = "float", ed_brake = "float" }, + params = { ramp_up = 0.5 }, -- numeric ones can be set per node as "lua.ramp_up" + state = { t = 0.0 }, -- copied into self, with the parameters + rate = 0, -- updates per second; 0 or none: every step + update = function(self, i, o, dt) -- dt: time since the previous update + o.traction = ... + end, +} +``` + +```json +{ "id": "demand", "type": "lua.block", "params": { "lua.ramp_up": 0.8 }, "lua": "block \"power_brake\" { ... }" } +``` + +- Every vehicle has a lua state of its own, separate from the scenery's one, and every block an environment of its own: + globals and changes to the libraries don't leak between blocks. +- Available: the basic functions, `string`, `table`, `math`, `bit` (luajit), `coroutine`. Not available: `io`, `os`, + `require`, `load`/`loadstring`/`dofile`, `debug`, `ffi`, `jit`. `print` writes to the log (at most 50 messages per block). +- `math.random` uses a generator seeded from the node id, so runs are repeatable; the editor uses the same generator. +- The jit compiler is off for these states and every call has an instruction budget (2 million), so an endless loop + stops its block instead of the simulator. +- A block whose script fails stops with a message in the log; its outputs keep their last values and the rest of the + vehicle keeps working. A script which fails while loading, or doesn't define the block, makes the file invalid. +- Outputs keep their values between updates until the script changes them. `int` values are rounded. +- Lua blocks need a simulator built with `WITH_LUA` (the default); other builds reject graphs with lua blocks. + +## Modules + +A module is a graph of logic kept in its own file (by convention `.vhgm`, the same format as `.vhg`) and used by +vehicle graphs as one block: + +```json +{ "id": "k1", "type": "module", "params": { "file": "modules/latch.vhgm" } } +``` + +- `file` is relative to the file of the graph using the module. +- The ports of the module are its `module.input` and `module.output` blocks; the id of the block is the name of the + port, `type` gives its type (`bool`, `int`, `float` or a physical domain, e.g. `high_voltage`), `label` a description. + A `module.input` has a `default` value, which the module sees when the input isn't connected. +- When the graph is loaded, the module node is replaced with the blocks of the module, with ids `/` + (e.g. `k1/sr`), and its connections are passed to the blocks connected to the interface blocks. The values in the + inspector, the live preview and the results of `-graph-run` use these ids. Modules can use other modules; a module + using itself, a missing file and a connection to an unknown port of a module are errors. Loops through modules need + a deferred block, as in the graph itself. +- The body and the profiles of a module are ignored. +- A module file can be run on its own with `-graph-run`: its inputs give their default values (or the forced ones from + the script) and its outputs have the state `value`. + +`tests/module.vhg` (with `tests/modules/`) uses two instances of a module with a nested module. ## Command line tools ``` eu07 -graph-catalog [-o ] -eu07 -graph-run [-script ] [-o ] [-dt ] [-t ] [-every ] +eu07 -graph-run [-script ] [-o ] [-dt ] [-t ] [-every ] [-state braked|ready] [-consist [:r]]... ``` -`-graph-catalog` writes the description of all block types, ports and parameters, for the external editor. +`-graph-catalog` writes the description of all block types, ports and parameters, the body entries, the interface +profiles, the commands of the simulator (`"commands": [{"name","target","mode"}]`, the names cab switches can be bound +to) and the preview protocol, for the external editor. -`-graph-run` loads and checks the graph, reports problems, and runs it without the simulator. The results are -the values of all signal outputs and the states of the blocks (e.g. `lamp:lit`), separated with `;`. +`-graph-run` loads and checks the graph, reports problems, and runs it without the simulator. As in the simulator, body +entries the graph doesn't give come from the `.fiz` file of the same name, if there is one. It builds the vehicle +body from the graph as the simulator does (`-state` chooses the initial state: `braked`, the default, as a vehicle +standing in a scenario, or `ready`), runs the blocks and moves the body along a straight level track without +neighbours. The results are the values of all signal outputs and the states of the blocks (e.g. `lamp:lit`), and for +graphs with a body section the speed (`body:v`, km/h), tractive and brake force (`body:ft`, `body:fb`, kN), brake pipe, +main reservoir and brake cylinder pressures (`body:pg`, `body:zg`, `body:bc`), separated with `;`. The script lists events, one per line: ``` @@ -90,6 +352,13 @@ The script lists events, one per line: Operations on cab switches: `press`, `release`, `toggle`, `inc`, `dec`, `set `, `value `. Operations on signal ports: `force `, `unforce `. +Consists: `-consist ` adds a vehicle behind the previous one, coupled by its front coupler; `-consist +:r` adds it reversed (coupled by its rear coupler). Repeatable. Relative paths are tried as given, then +relative to the first vehicle's file. All coupling flags are set (brake pipes connect); vehicle 1 has the occupied +cab; each vehicle moves on its own (no coupler forces). Script targets of vehicle k ≥ 2 are prefixed `k:` (e.g. +`1.0 force 2:dir.q 1`, `2.0 press 3:button`); its result columns are prefixed `k:` and follow the columns of vehicle 1. +Graphs with more than one `el.hsb` get a `body:mains` column (Mains of the body). + On Windows the simulator runs without a console, so use `-o` for the results, and redirect the output to a file to see the messages, e.g. `eu07.exe -graph-run test.vhg > log.txt`. @@ -100,8 +369,29 @@ python3 tools/vehicle_graph/run_tests.py [--u ``` - `tests/*.vhg` start with `// expect: ok`, `// expect: error ` or `// expect: warning `; -- every `.vhg` with a `.script` is run twice; both runs have to give identical results, equal to `.expected.csv`. +- every `.vhg` with a `.script` is run twice; both runs have to give identical results, equal to `.expected.csv` + within a relative tolerance of 1e-6 (the body computations may differ in the last digits between compilers). + A script line `# options: ...` adds options of `-graph-run`, e.g. `# options: -state ready -every 100`. `--update` replaces the expected results with the current ones, after checking the change. -`examples/` holds the cab logic of two vehicles, modelled after the 303E (ET22, resistor starting) and Vectron -(induction motors) `.fiz` files. +`examples/` holds two whole vehicles built from their `.fiz` files: `303e.vhg` (ET22: series DC motors with resistor +starting, LSt, FV4a, FD1) and `vectron.vhg` (induction motors, EStED with electrodynamic brake blending, MHZ_K8P, +spring brake); their scripts start the locomotive from the cab, drive off and stop with the train brake. The +`*-logic.vhg` files are the cab logic of the same vehicles from the first stage. `tests/brakes.vhg` charges the +brake pipe of a locomotive with a train of wagons and brakes it. `tests/diesel-mech.vhg`, `diesel-hydro.vhg` and +`diesel-electric.vhg` start a diesel engine and drive off with each transmission; `multisystem.vhg` switches between two +supply systems with two breakers; `consist.vhg` (with `consist-short.vhg`) runs a consist of four vehicles, one reversed, +connected with `if.line`; `anim.vhg` computes the `veh.anim` and `veh.visible` states. + +The tests need a simulator built with lua support. The external editor runs the same files with its `Checks` project +and has to give the same results. + +## Checking the code shared with the .fiz vehicles + +The code moved out of `TMoverParameters` for the blocks (series motor equations, the induction motor and inverter +computation, the brake rigging force, the feed pipe exchange, the brake valve factories) is called by the `.fiz` +vehicles as before. Configure with `-DVGRAPH_SHADOW_CHECK=ON` to run each moved function side by side with its +original form (`McZapkie/Mover_shadow.cpp`) on copies of the state; every difference goes to the log as +`VGRAPH_SHADOW_CHECK: : differs`. Drive the reference vehicles (series motor locomotives and EMUs, +induction motor locomotives and EMUs with and without speed control and blending, wagons with ESt3, LSt, EP2, KE) +and check the log; then the original forms can be removed. diff --git a/tools/vehicle_graph/examples/303e.expected.csv b/tools/vehicle_graph/examples/303e.expected.csv new file mode 100644 index 00000000..e181349d --- /dev/null +++ b/tools/vehicle_graph/examples/303e.expected.csv @@ -0,0 +1,132 @@ +time;bat.pos;bat.index;pant_f.pos;pant_f.index;pant_r.pos;pant_r.index;ws_on.pos;ws_on.index;ws_off.pos;ws_off.index;conv_sw.pos;conv_sw.index;comp_sw.pos;comp_sw.index;heat_sw.pos;heat_sw.index;high_current.pos;high_current.index;relay_reset.pos;relay_reset.index;reverser.pos;reverser.index;mainctrl.pos;mainctrl.index;shunt.pos;shunt.index;brake.pos;brake.index;local.pos;local.index;ca_reset.pos;ca_reset.index;sand_bt.pos;sand_bt.index;horn_lo.pos;horn_lo.index;horn_hi.pos;horn_hi.index;lights_sw.pos;lights_sw.index;pant_f_up.q;pant_r_up.q;pf.up;pf.height;pf.u;pr.up;pr.height;pr.u;ws_close.q;ws.closed;ws.i;ws.tripped;dir_set.q;power_req.q;no_overload.q;line_req.q;line_req2.q;line.closed;motors.i;motors.u_motor;motors.overload;axles.force;axles.slip;conv.run;batt.ok;batt.u;heat.i;comp_on.q;comp.run;gov.need;res.p;fv4a.p_ctl;pipe.p;dist.bc;dist.aux;dist.crp;rig.force;vs.v;vs.v_signed;vs.accel;vs.wheel_rpm;vs.slipping;vs.cab;vs.time;ca.lamp;ca.buzzer;ca.brake;ca.shp_lamp;sand.sanding;pc1.q;pc_front.q;ws_open.q;ws_lamp.q;line_open.q;line_lamp.q;ovl_lamp.q;conv_off.q;conv_lamp.q;slip_lamp.q;hv_volts.q;bat:position;pant_f:position;pant_r:position;ws_on:position;ws_off:position;conv_sw:position;comp_sw:position;heat_sw:position;high_current:position;relay_reset:position;reverser:position;mainctrl:position;shunt:position;brake:position;local:position;ca_reset:position;sand_bt:position;horn_lo:position;horn_hi:position;lights_sw:position;pf:height;pr:height;ws:closed;motors:overload;batt:charge;gov:need;lamp_ws:lit;lamp_line:lit;lamp_ovl:lit;lamp_conv:lit;lamp_slip:lit;lamp_ca:lit;lamp_shp:lit;g_hv:value;g_i:value;g_v:value;g_pipes:value;g_pipes:value2;g_bc:value;snd_conv:playing;snd_conv:volume;snd_conv:pitch;snd_comp:playing;snd_comp:volume;snd_comp:pitch;snd_buzzer:playing;snd_buzzer:volume;snd_buzzer:pitch;snd_horn_lo:playing;snd_horn_lo:volume;snd_horn_lo:pitch;snd_horn_hi:playing;snd_horn_hi:volume;snd_horn_hi:pitch;snd_ws:playing;snd_ws:volume;snd_ws:pitch;body:v;body:ft;body:fb;body:pg;body:zg;body:bc +1;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0.125;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;1;110;0;0;0;1;7.34859349;0;4.99734854;0;7.99992806;4.99349798;0;0;0;0;0;0;1;1;1;1;0;1;0;21;0;1;1;1;1;0;1;1;0;0;1;1;0;0;0;0;0;0;0;0;1;0;0;2;0;0;0;0;0;0;0.125;0;0;0;100;1;1;1;0;1;0;1;1;0;0;0;4.99734831;7.34859371;0;0;1;1;0;1;1;1;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99735275;7.34859349;0 +2;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0.375;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;1;110;0;0;0;1;7.34724402;0;4.99834687;0;7.99985612;4.99430846;0;0;0;0;0;0;1;2;0;0;0;0;0;21;0;1;1;1;1;0;1;1;0;0;1;1;0;0;0;0;0;0;0;0;1;0;0;2;0;0;0;0;0;0;0.375;0;0;0;100;1;1;1;0;1;0;0;0;0;0;0;4.99834681;7.34724379;0;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99835222;7.34724402;0 +3;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0.625;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;1;110;0;0;0;1;7.34590576;0;4.99867777;0;7.99978418;4.99509784;0;0;0;0;0;0;1;3;0;0;0;0;0;21;0;1;1;1;1;0;1;1;0;0;1;1;0;0;0;0;0;0;0;0;1;0;0;2;0;0;0;0;0;0;0.625;0;0;0;100;1;1;1;0;1;0;0;0;0;0;0;4.99867773;7.34590578;0;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99868287;7.34590576;0 +4;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0.875;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;1;110;0;0;0;1;7.34456961;0;4.99884038;0;7.99971224;4.99582778;0;0;0;0;0;0;1;4;0;0;0;0;0;21;0;1;1;1;1;0;1;1;0;0;1;1;0;0;0;0;0;0;0;0;1;0;0;2;0;0;0;0;0;0;0.875;0;0;0;100;1;1;1;0;1;0;0;0;0;0;0;4.99884033;7.34456968;0;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99884499;7.34456961;0 +5;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;1;110;0;0;0;1;7.34323372;0;4.99893456;0;7.9996403;4.99648708;0;0;0;0;0;0;1;5;0;0;0;0;0;21;0;1;1;1;1;0;1;1;0;3000;1;1;0;0;0;0;0;0;0;0;1;0;0;2;0;0;0;0;0;0;1;0;0;0;100;1;1;1;0;1;0;0;0;3000;0;0;4.99893475;7.34323359;0;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.9989386;7.34323372;0 +6;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;1;110;0;0;0;1;7.34189746;0;4.99899394;0;7.99956836;4.99707084;0;0;0;0;0;0;1;6;0;0;0;0;0;21;0;1;1;1;1;0;1;1;0;3000;1;1;0;0;0;0;0;0;0;0;1;0;0;2;0;0;0;0;0;0;1;0;0;0;100;1;1;1;0;1;0;0;0;3000;0;0;4.99899387;7.34189749;0;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99899736;7.34189746;0 +7;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0;0;0;1;110;0;0;0;1;7.34056053;0;4.99903315;0;7.99949642;4.99757654;0;0;0;0;0;0;1;7;0;0;0;0;0;21;0;0;0;1;1;0;1;1;0;3000;1;1;0;0;0;0;0;0;0;0;1;0;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;1;0;1;0;0;0;3000;0;0;4.99903297;7.34056044;0;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;0;4.99903592;7.34056053;0 +8;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0;0;0;1;110;0;0;0;1;7.33922279;0;4.9990596;0;7.99942448;4.99800286;0;0;0;0;0;0;1;8;0;0;0;0;0;21;0;0;0;1;1;0;1;1;0;3000;1;1;0;0;0;1;0;0;0;0;1;0;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;1;0;1;0;0;0;3000;0;0;4.99905968;7.33922291;0;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;0;4.99906171;7.33922279;0 +9;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0;0;0;1;110;0;0;0;1;7.33788414;0;4.99907749;0;7.99935254;4.99834925;0;0;0;0;0;0;1;9;0;0;0;0;0;21;0;0;0;1;1;0;1;1;0;3000;1;1;0;0;0;1;0;0;0;0;1;0;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;1;0;1;0;0;0;3000;0;0;4.99907732;7.33788395;0;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;0;4.99907892;7.33788414;0 +10;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0;0;0;1;110;0;0;0;1;7.33654451;0;4.99908941;0;7.99928061;4.99861585;0;0;0;0;0;0;1;10;0;0;0;0;0;21;0;0;0;1;1;0;1;1;0;3000;1;1;0;0;0;1;0;0;0;0;1;0;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;1;0;1;0;0;0;3000;0;0;4.99908924;7.33654451;0;0;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;0;4.99909016;7.33654451;0 +11;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;0;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;1;7.33520389;0;4.99909713;0;7.99920867;4.99880376;0;0;0;0;0;0;1;11;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;0;0;0;0;1;0;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;1;0;0;0;0;0;3000;0;0;4.99909735;7.33520365;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;0;4.9990972;7.33520389;0 +12;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;0;1;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;5.66666667;0;0;0;1;0;0;0;0;0;0;0;0;1;1;110;0;1;1;1;7.41007645;0;4.99911282;0;7.99913674;4.99891869;0;0;0;0;0;0;1;12;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;1;0;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;1;0;0;0;0;0;3000;0;0;4.99911261;7.41007662;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;0;4.99911231;7.41007645;0 +13;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;5.66666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;1;1;1;7.48434298;0;4.99913165;0;7.9990648;4.99897934;0;0;0;0;0;0;1;13;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;1;0;0;0;0;0;3000;0;0;4.99913168;7.48434305;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;0;4.99913082;7.48434298;0 +14;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;5.66666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;1;1;1;7.55802282;0;4.9991534;0;7.99899287;4.99901552;0;0;0;0;0;0;1;14;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;1;0;0;0;0;0;3000;0;0;4.99915361;7.55802298;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;0;4.99915255;7.55802282;0 +15;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;1;1;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;119.014173;0;1;1;1;1;1;1;113.295343;30.8294871;0;28374.4016;0;1;1;110;0;1;1;1;7.63112098;0;4.99917489;0;7.99892094;4.99904725;0;0.855775276;0.237715355;0.265650724;3.63202308;0;1;15;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;1;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;113.295341;0.855775297;4.99917507;7.63112116;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0.865334894;28.3744016;0;4.9991741;7.63112098;0 +16;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;2;2;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;161.702722;0;1;1;1;1;1;1;155.887279;74.9691122;0;46537.2476;0;1;1;110;0;1;1;1;7.7036421;0;4.99919451;0;7.998849;4.99907638;0;2.5243606;0.701211277;0.448381708;10.7137191;0;1;16;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;2;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;155.887283;2.52436066;4.99919462;7.70364189;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;2.54049033;46.5372476;0;4.99919377;7.7036421;0 +17;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;3;3;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;196.876338;0;1;1;1;1;1;1;190.922552;139.000103;0;62567.1071;0;1;1;110;0;1;1;1;7.77559072;0;4.99921254;0;7.99877707;4.99910307;0;4.83978265;1.34438407;0.609713143;20.5406755;0;1;17;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;3;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;190.922546;4.83978271;4.99921274;7.7755909;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;4.86170799;62.5671071;0;4.99921185;7.77559072;0 +18;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;3;3;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;183.707332;0;1;1;1;1;1;1;177.800131;180.99513;0;56474.2277;0;1;1;110;0;1;1;1;7.84697137;0;4.9992292;0;7.99870514;4.99912763;0;6.91937549;1.92204875;0.548195433;29.3667417;0;1;18;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;3;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;177.800125;6.91937542;4.99922943;7.84697151;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;6.93909043;56.4742277;0;4.99922856;7.84697137;0 +19;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;4;4;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;201.742241;0;1;1;1;1;1;1;195.735591;247.399937;0;64825.008;0;1;1;110;0;1;1;1;7.91778851;0;4.99924466;0;7.99863321;4.99915031;0;9.34444429;2.59567897;0.632104436;39.6590533;0;1;19;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;4;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;195.735596;9.34444427;4.99924469;7.91778851;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;9.36717105;64.825008;0;4.99924407;7.91778851;0 +20;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;4;4;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;186.394042;0;1;1;1;1;1;1;180.451666;286.645236;0;57697.517;0;1;1;110;0;1;1;1;7.98804659;0;4.99925907;0;7.99856128;4.99917133;0;11.4840661;3.19001837;0.560008039;48.7398905;0;1;20;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;4;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;180.45166;11.484066;4.999259;7.98804665;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;11.5042033;57.697517;0;4.99925851;7.98804659;0 +21;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;5;5;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;193.900746;0;1;1;1;1;1;1;187.903952;345.489768;0;61157.139;0;1;1;110;0;1;1;1;8.0571281;0;4.99927236;0;8.00042587;4.99919076;0;13.7711304;3.825314;0.594522515;58.4464927;0;1;21;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;5;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;187.903946;13.7711306;4.99927235;8.05712795;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;13.7925053;61.157139;0;4.99927184;8.0571281;0 +22;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;5;5;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;179.104123;0;1;1;1;1;1;1;173.173562;377.200614;0;54349.8762;0;1;1;110;0;1;1;1;8.12396148;0;4.99928462;0;8.00746819;4.99920864;0;15.7810188;4.38361634;0.525541049;66.9767241;0;1;22;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;5;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;173.173569;15.7810192;4.99928474;8.12396145;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;15.7999163;54.3498762;0;4.99928413;8.12396148;0 +23;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;6;6;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;181.052141;0;1;1;1;1;1;1;175.098842;425.410522;0;55232.3008;0;1;1;110;0;1;1;1;8.18871725;0;4.99929601;0;8.01921383;4.99922517;0;17.8298859;4.95274608;0.533999784;75.672386;0;1;23;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;6;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;175.098846;17.8298855;4.99929619;8.18871689;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;17.8490859;55.2323008;0;4.99929555;8.18871725;0 +24;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;6;6;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;168.200358;0;1;1;1;1;1;1;162.304295;449.128414;0;49413.1793;0;1;1;110;0;1;1;1;8.25157776;0;4.99930663;0;8.03515908;4.99924051;0;19.6406279;5.45572998;0.474917034;83.3574139;0;1;24;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;6;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;162.304291;19.6406288;4.99930668;8.25157738;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;19.6577061;49.4131793;0;4.9993062;8.25157776;0 +25;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;8;8;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;167.029838;0;1;1;1;1;1;1;161.130926;486.268957;0;48885.0693;0;1;1;110;0;1;1;1;8.31270869;0;4.99931656;0;8.05484577;4.99925477;0;21.4300415;5.9527893;0.46911963;90.9519209;0;1;25;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;8;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;161.13092;21.4300423;4.99931669;8.31270885;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;21.4469108;48.8850693;0;4.99931616;8.31270869;0 +26;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;8;8;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;156.550466;0;1;1;1;1;1;1;150.695441;503.349236;0;44233.2269;0;1;1;110;0;1;1;1;8.37254556;0;4.99932593;0;8.0769781;4.99926809;0;23.0296124;6.39711457;0.421781207;97.7407109;0;1;26;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;8;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;150.695435;23.0296116;4.99932575;8.37254524;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;23.0447814;44.2332269;0;4.99932555;8.37254556;0 +27;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;8;8;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;147.966093;0;1;1;1;1;1;1;142.143501;517.346625;0;40486.1977;0;1;1;110;0;1;1;1;8.43142636;0;4.99933479;0;8.1005778;4.99928059;0;24.4762467;6.79895741;0.383581472;103.880417;0;1;27;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;8;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;142.143494;24.4762459;4.99933481;8.43142605;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;24.4900432;40.4861977;0;4.99933444;8.43142636;0 +28;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;10;10;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;148.047303;0;1;1;1;1;1;1;142.220206;546.945101;0;40519.5253;0;1;1;110;0;1;1;1;8.48941164;0;4.99934318;0;8.12548423;4.99929231;0;25.9230827;7.2008563;0.383434856;110.02098;0;1;28;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;10;0;2;0;0;0;0;0;0;1;0;1;0;100;1;0;0;0;0;0;0;0;3000;142.2202;25.9230824;4.9993434;8.48941135;0;1;1;1;1;1;1;0;1;1;0;1;1;0;1;1;1;1;1;25.9368736;40.5195253;0;4.99934285;8.48941164;0 +29;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;10;10;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;135.699818;0;1;1;1;1;1;1;134.89899;557.397967;0;37362.9794;0;1;1;110;0;0;0;0;8.49243378;0;4.99934654;0;8.15082774;4.9993002;0;27.2429393;7.56748314;0.351175528;115.622625;0;1;29;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;10;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;134.898987;27.242939;4.99934673;8.49243355;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;27.2555711;37.3629794;0;4.99934617;8.49243378;0 +30;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;10;10;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;129.505991;0;1;1;1;1;1;1;128.725281;566.21248;0;34741.7578;0;1;1;110;0;0;0;0;8.48306425;0;4.99934863;0;8.17499455;4.99930525;0;28.4569557;7.90470993;0.324328676;120.775071;0;1;30;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;10;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;128.725281;28.456955;4.99934864;8.48306465;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;28.4686227;34.7417578;0;4.99934829;8.48306425;0 +31;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;12;12;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;129.829991;0;1;1;1;1;1;1;129.045938;590.531482;0;34876.9363;0;1;1;110;0;0;0;0;8.47410302;0;4.99935049;0;8.19792583;4.9993099;0;29.6746641;8.24296226;0.325217402;125.943186;0;1;31;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;12;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;129.045944;29.6746635;4.99935055;8.47410297;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;29.6863628;34.8769363;0;4.99935017;8.47410302;0 +32;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;12;12;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;124.388641;0;1;1;1;1;1;1;123.621359;597.234906;0;32605.0578;0;1;1;110;0;0;0;0;8.46557002;0;4.99935223;0;8.21955729;4.99931418;0;30.801871;8.55607528;0.301886119;130.727201;0;1;32;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;12;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;123.621361;30.8018703;4.99935246;8.46557045;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;30.8127312;32.6050578;0;4.99935193;8.46557002;0 +33;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;12;12;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;119.695004;0;1;1;1;1;1;1;118.940976;603.018689;0;30671.504;0;1;1;110;0;0;0;0;8.45773485;0;4.99935388;0;8.23906942;4.99931817;0;31.8515649;8.84765692;0.281979732;135.18224;0;1;33;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;12;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;118.940979;31.8515644;4.99935389;8.45773506;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;31.8617095;30.671504;0;4.9993536;8.45773485;0 +34;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;12;12;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;115.597578;0;1;1;1;1;1;1;114.854218;608.0689;0;29004.6771;0;1;1;110;0;0;0;0;8.45062426;0;4.99935546;0;8.25639156;4.99932187;0;32.8346915;9.12074764;0.264774815;139.354759;0;1;34;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;12;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;114.854218;32.8346901;4.99935532;8.45062447;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;32.8442176;29.0046771;0;4.99935519;8.45062426;0 +35;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;14;14;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;116.037294;0;1;1;1;1;1;1;115.291769;627.35838;0;29182.143;0;1;1;110;0;0;0;0;8.44416063;0;4.99935695;0;8.27175834;4.99932531;0;33.8229092;9.39525256;0.266118985;143.548885;0;1;35;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;14;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;115.291771;33.8229103;4.99935675;8.44416046;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;33.8324835;29.182143;0;4.9993567;8.44416063;0 +36;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;14;14;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;112.328412;0;1;1;1;1;1;1;111.592124;631.293878;0;27689.3744;0;1;1;110;0;0;0;0;8.43827472;0;4.99935838;0;8.28537904;4.99932851;0;34.7522247;9.65339576;0.250665925;147.493023;0;1;36;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;14;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;111.592125;34.752224;4.99935818;8.43827438;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;34.7612435;27.6893744;0;4.99935814;8.43827472;0 +37;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;14;14;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;109.039669;0;1;1;1;1;1;1;108.310989;634.784185;0;26380.6635;0;1;1;110;0;0;0;0;8.43290476;0;4.99935974;0;8.29744031;4.9993315;0;35.6294175;9.89706043;0.237079909;151.215945;0;1;37;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;14;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;108.310989;35.6294174;4.99935961;8.4329052;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;35.6379478;26.3806635;0;4.99935951;8.43290476;0 +38;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;14;14;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;106.099165;0;1;1;1;1;1;1;105.376834;637.905393;0;25223.0151;0;1;1;110;0;0;0;0;8.42799565;0;4.99936103;0;8.30810874;4.99933428;0;36.4605758;10.1279377;0.225027293;154.74349;0;1;38;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;14;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;105.376831;36.4605751;4.99936104;8.42799568;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;36.4686727;25.2230151;0;4.99936082;8.42799565;0 +39;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;16;16;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;106.548081;0;1;1;1;1;1;1;105.824273;653.990929;0;25398.7547;0;1;1;110;0;0;0;0;8.42349821;0;4.99936227;0;8.31753297;4.99933688;0;37.29673;10.3602028;0.226380869;158.292238;0;1;39;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;16;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;105.824272;37.29673;4.99936247;8.42349815;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;37.3048755;25.3987547;0;4.99936206;8.42349821;0 +40;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;16;16;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;103.833309;0;1;1;1;1;1;1;103.115147;656.448783;0;24339.1785;0;1;1;110;0;0;0;0;8.41936854;0;4.99936345;0;8.32584569;4.9993393;0;38.0912159;10.5808933;0.215315699;161.664141;0;1;40;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;16;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;103.11515;38.091217;4.99936342;8.41936874;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;38.0989635;24.3391785;0;4.99936325;8.41936854;0 +41;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;16;16;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;101.379069;0;1;1;1;1;1;1;100.665697;658.671046;0;23390.6185;0;1;1;110;0;0;0;0;8.41556743;0;4.99936458;0;8.33316536;4.99934157;0;38.847978;10.791105;0.205379475;164.875939;0;1;41;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;16;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;100.665695;38.8479767;4.99936438;8.4155674;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;38.8553682;23.3906185;0;4.99936439;8.41556743;0 +42;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;16;16;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;99.1469769;0;1;1;1;1;1;1;98.437711;660.692387;0;22535.9009;0;1;1;110;0;0;0;0;8.41205991;0;4.99936565;0;8.33959774;4.99934369;0;39.5707524;10.9918757;0.196398359;167.943489;0;1;42;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;16;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;98.4377136;39.5707512;4.99936581;8.41205978;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;39.5778197;22.5359009;0;4.99936548;8.41205991;0 +43;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;18;18;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;99.6000336;0;1;1;1;1;1;1;98.8896632;674.670799;0;22708.6471;0;1;1;110;0;0;0;0;8.40881472;0;4.99936669;0;8.34523729;4.99934568;0;40.2984442;11.1940123;0.197744966;171.031909;0;1;43;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;18;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;98.8896637;40.2984428;4.99936676;8.40881443;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;40.3055599;22.7086471;0;4.99936652;8.40881472;0 +44;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;18;18;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;97.5084699;0;1;1;1;1;1;1;96.8018286;676.261207;0;21913.3847;0;1;1;110;0;0;0;0;8.40580394;0;4.99936768;0;8.35016838;4.99934754;0;40.9948488;11.387458;0.189362691;173.987542;0;1;44;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;18;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;96.8018265;40.9948502;4.99936771;8.40580368;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;41.001663;21.9133847;0;4.99936751;8.40580394;0 +45;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;18;18;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;95.5908782;0;1;1;1;1;1;1;94.887473;677.719467;0;21190.4899;0;1;1;110;0;0;0;0;8.40300266;0;4.99936862;0;8.35446633;4.99934929;0;41.6624519;11.5729033;0.181718474;176.820938;0;1;45;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;18;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;94.8874741;41.6624527;4.99936867;8.40300274;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;41.6689912;21.1904899;0;4.99936846;8.40300266;0 +46;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;18;18;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;93.8248069;0;1;1;1;1;1;1;93.1242309;679.062617;0;20530.136;0;1;1;110;0;0;0;0;8.4003886;0;4.99936953;0;8.35819846;4.99935092;0;42.3037236;11.7510343;0.174712726;179.542579;0;1;46;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;18;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;93.1242294;42.3037224;4.99936962;8.40038872;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;42.3100109;20.530136;0;4.99936938;8.4003886;0 +47;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;94.1715206;0;1;1;1;1;1;1;93.4702465;690.996907;0;20659.3015;0;1;1;110;0;0;0;0;8.39794187;0;4.9993704;0;8.3614249;4.99935246;0;42.9483284;11.9300912;0.1756418;182.278367;0;1;47;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;93.4702454;42.9483299;4.99937057;8.39794159;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;42.954649;20.6593015;0;4.99937025;8.39794187;0 +48;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;92.5060039;0;1;1;1;1;1;1;91.8073208;692.046629;0;20040.4519;0;1;1;110;0;0;0;0;8.39564469;0;4.99937123;0;8.36419936;4.99935391;0;43.5685015;12.1023615;0.169054988;184.910463;0;1;48;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;91.8073196;43.5685005;4.99937105;8.39564514;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;43.5745852;20.0404519;0;4.99937109;8.39564469;0 +49;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;90.9624032;0;1;1;1;1;1;1;90.2660084;693.019582;0;19471.2314;0;1;1;110;0;0;0;0;8.39348119;0;4.99937203;0;8.36656986;4.99935527;0;44.1659052;12.268307;0.162976002;187.445924;0;1;49;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;90.2660065;44.165905;4.99937201;8.39348125;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;44.1717703;19.4712314;0;4.9993719;8.39348119;0 +50;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;89.5267751;0;1;1;1;1;1;1;88.8324129;693.924539;0;18945.657;0;1;1;110;0;0;0;0;8.39143717;0;4.9993728;0;8.36857927;4.99935656;0;44.7422541;12.4284039;0.15734412;189.892024;0;1;50;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;88.8324127;44.7422523;4.99937296;8.39143753;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;44.7479165;18.945657;0;4.99937267;8.39143717;0 +51;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;88.1873037;0;1;1;1;1;1;1;87.4947565;694.768935;0;18458.69;0;1;1;110;0;0;0;0;8.38949995;0;4.99937354;0;8.37026592;4.99935776;0;45.2990621;12.5830728;0.152108182;192.255191;0;1;51;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;87.4947586;45.2990608;4.99937344;8.38949966;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;45.3045362;18.45869;0;4.99937341;8.38949995;0 +52;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;86.933902;0;1;1;1;1;1;1;86.2429836;695.559117;0;18006.0552;0;1;1;110;0;0;0;0;8.3876582;0;4.99937425;0;8.37166407;4.9993589;0;45.8376737;12.7326871;0.147224761;194.541129;0;1;52;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;86.242981;45.8376732;4.99937439;8.38765812;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;45.842972;18.0060552;0;4.99937412;8.3876582;0 +53;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;85.7579009;0;1;1;1;1;1;1;85.0684504;696.300541;0;17584.0994;0;1;1;110;0;0;0;0;8.3859018;0;4.99937493;0;8.37280433;4.99935998;0;46.3592886;12.8775802;0.142656732;196.754932;0;1;53;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;85.0684509;46.3592873;4.99937487;8.38590145;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;46.3644226;17.5840994;0;4.9993748;8.3859018;0 +54;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;84.6518033;0;1;1;1;1;1;1;83.9636814;696.997926;0;17189.6817;0;1;1;110;0;0;0;0;8.38422169;0;4.99937558;0;8.37371404;4.999361;0;46.8649828;13.0180508;0.138372158;198.901164;0;1;54;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;83.9636841;46.8649826;4.99937534;8.38422203;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;46.8699627;17.1896817;0;4.99937546;8.38422169;0 +55;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;83.6090886;0;1;1;1;1;1;1;82.9221738;697.655378;0;16820.085;0;1;1;110;0;0;0;0;8.38260978;0;4.99937621;0;8.37441767;4.99936196;0;47.3557253;13.1543681;0.134343401;200.98394;0;1;55;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;82.9221725;47.3557243;4.9993763;8.38260937;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;47.3605603;16.820085;0;4.99937609;8.38260978;0 +56;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;82.6240554;0;1;1;1;1;1;1;81.938241;698.276485;0;16472.9465;0;1;1;110;0;0;0;0;8.38105885;0;4.99937681;0;8.37493706;4.99936287;0;47.8323924;13.2867757;0.130546411;203.006978;0;1;56;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;81.9382401;47.8323936;4.99937677;8.38105869;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;47.8370907;16.4729465;0;4.9993767;8.38105885;0 +57;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;81.691694;0;1;1;1;1;1;1;81.0068861;698.864403;0;16146.2008;0;1;1;110;0;0;0;0;8.37956242;0;4.9993774;0;8.37529175;4.99936373;0;48.2957794;13.4154943;0.126960158;204.973654;0;1;57;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;81.0068893;48.2957802;4.99937725;8.37956238;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;48.3003487;16.1462008;0;4.99937728;8.37956242;0 +58;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;80.8075824;0;1;1;1;1;1;1;80.123698;699.421916;0;15838.0341;0;1;1;110;0;0;0;0;8.37811473;0;4.99937796;0;8.3754992;4.99936454;0;48.7466105;13.5407251;0.123566161;206.887041;0;1;58;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;80.1236954;48.7466087;4.99937773;8.3781147;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;48.7510577;15.8380341;0;4.99937784;8.37811473;0 +59;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;79.9678007;0;1;1;1;1;1;1;79.2847659;699.951492;0;15546.8465;0;1;1;110;0;0;0;0;8.3767106;0;4.99937849;0;8.37557501;4.99936532;0;49.1855478;13.6626522;0.120348111;208.749948;0;1;59;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;79.2847672;49.1855469;4.99937868;8.37671089;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;49.1898792;15.5468465;0;4.99937838;8.3767106;0 +60;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;79.1688601;0;1;1;1;1;1;1;78.4866092;700.455328;0;15271.2209;0;1;1;110;0;0;0;0;8.37534481;0;4.99937901;0;8.37553525;4.99936605;0;49.6131977;13.7814438;0.117291554;210.564951;0;1;60;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;78.4866104;49.6131973;4.99937916;8.37534523;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;49.6174191;15.2712209;0;4.9993789;8.37534481;0 +61;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;78.4076441;0;1;1;1;1;1;1;77.7261182;700.935388;0;15009.8975;0;1;1;110;0;0;0;0;8.37399111;0;4.99937951;0;8.37545993;4.99936674;0;50.0301179;13.897255;0.114383633;212.334415;0;1;61;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;77.72612;50.030117;4.99937963;8.37399101;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;50.0342347;15.0098975;0;4.99937939;8.37399111;0 +62;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;77.6813588;0;1;1;1;1;1;1;77.000505;701.393431;0;14761.752;0;1;1;110;0;0;0;0;8.37263743;0;4.99937999;0;8.37538461;4.99936739;0;50.4368222;14.0102284;0.111612866;214.060522;0;1;62;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;77.0005035;50.436821;4.99938011;8.37263775;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;50.4408393;14.761752;0;4.99937987;8.37263743;0 +63;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;76.9874913;0;1;1;1;1;1;1;76.307262;701.831041;0;14525.7777;0;1;1;110;0;0;0;0;8.37128376;0;4.99938044;0;8.37530929;4.99936801;0;50.8337853;14.1204959;0.10896897;215.745286;0;1;63;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;76.3072586;50.833786;4.99938059;8.37128353;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;50.8377073;14.5257777;0;4.99938032;8.37128376;0 +64;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;76.3237746;0;1;1;1;1;1;1;75.6441267;702.249645;0;14301.0706;0;1;1;110;0;0;0;0;8.36993012;0;4.99938088;0;8.37523398;4.9993686;0;51.2214469;14.2281797;0.106442699;217.390572;0;1;64;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;75.6441269;51.221447;4.99938107;8.36993027;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;51.2252779;14.3010706;0;4.99938076;8.36993012;0 +65;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;75.6881573;0;1;1;1;1;1;1;75.0090518;702.650536;0;14086.816;0;1;1;110;0;0;0;0;8.36857649;0;4.99938129;0;8.37515866;4.99936916;0;51.6002149;14.333393;0.10402572;218.998114;0;1;65;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;75.0090485;51.6002159;4.99938107;8.36857605;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;51.603959;14.086816;0;4.99938117;8.36857649;0 +66;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;75.0787781;0;1;1;1;1;1;1;74.4001796;703.034887;0;13882.2778;0;1;1;110;0;0;0;0;8.36722288;0;4.99938169;0;8.37508334;4.99936969;0;51.9704689;14.4362414;0.101710498;220.569521;0;1;66;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;74.400177;51.9704704;4.99938154;8.36722279;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;51.9741297;13.8822778;0;4.99938157;8.36722288;0 +67;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;74.4939436;0;1;1;1;1;1;1;73.8158198;703.403764;0;13686.7893;0;1;1;110;0;0;0;0;8.36586929;0;4.99938208;0;8.37500803;4.9993702;0;52.3325627;14.536823;0.0994902036;222.106294;0;1;67;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;73.8158188;52.3325615;4.99938202;8.36586952;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;52.3361436;13.6867893;0;4.99938196;8.36586929;0 +68;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;73.9321096;0;1;1;1;1;1;1;73.2544309;703.75814;0;13499.7447;0;1;1;110;0;0;0;0;8.36451571;0;4.99938244;0;8.37493271;4.99937067;0;52.6868265;14.6352296;0.097358628;223.609837;0;1;68;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;73.2544327;52.6868248;4.9993825;8.3645153;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;52.6903307;13.4997447;0;4.99938233;8.36451571;0 +69;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;73.3918643;0;1;1;1;1;1;1;72.7146038;704.098906;0;13320.5925;0;1;1;110;0;0;0;0;8.36316215;0;4.9993828;0;8.3748574;4.99937113;0;53.0335693;14.731547;0.0953101146;225.081459;0;1;69;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;72.7146072;53.0335693;4.99938297;8.36316204;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;53.0369997;13.3205925;0;4.99938268;8.36316215;0 +70;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;20;20;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;72.8719143;0;1;1;1;1;1;1;72.1950473;704.426876;0;13148.8296;0;1;1;110;0;0;0;0;8.36180861;0;4.99938313;0;8.37478209;4.99937156;0;53.3730805;14.8258557;0.0933394975;226.522389;0;1;70;0;0;0;0;0;21;0;0;0;0;0;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;20;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;0;0;0;0;0;0;3000;72.1950455;53.3730812;4.99938297;8.36180878;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;53.37644;13.1488296;0;4.99938302;8.36180861;0 +71;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.36045509;0;4.99938346;0;8.37470678;4.99937197;0;53.2382207;14.7883946;-0.0387329248;225.950026;0;1;71;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;53.2382202;4.99938345;8.36045551;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;53.2368264;0;0;4.99938334;8.36045509;0 +72;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.35916475;0;4.65068642;0;8.37463146;4.99549875;0;53.0989639;14.7497122;-0.0386330671;225.359002;0;1;72;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;53.0989647;4.65068626;8.35916519;0;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;53.0975731;0;0;4.6514983;8.35916475;0 +73;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.35731854;0;4.31371038;0.490211723;8.28830406;4.98716331;6108.50212;52.9286667;14.7024074;-0.0988213441;224.636238;0;1;73;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;52.9286652;4.31371021;8.35731888;0.490211725;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;52.9250702;0;6.10850212;4.31438984;8.35731854;0.490211723 +74;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.35389194;0;4.0598102;0.944901431;8.26786572;4.98716331;17567.673;52.3639416;14.5455393;-0.213545314;222.239471;0;1;74;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;52.3639412;4.05981016;8.35389233;0.944901407;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;52.3562135;0;17.567673;4.06193166;8.35389194;0.944901431 +75;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.35000117;0;3.90851346;1.41391184;8.24798583;4.98716331;28230.5616;51.3975394;14.2770943;-0.320157883;218.137932;0;1;75;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;51.397541;3.90851355;8.35000134;1.41391182;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;51.3859777;0;28.2305616;3.90925987;8.35000117;1.41391184 +76;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.34570734;0;3.84400891;1.85610551;8.23090683;4.98716331;37504.5923;50.0723579;13.9089883;-0.412606347;212.513687;0;1;76;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;50.0723572;3.84400892;8.34570694;1.85610557;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;50.0574734;0;37.5045923;3.84475783;8.34570734;1.85610551 +77;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.34111844;0;3.8268758;2.24319601;8.21797117;4.98716331;45320.2546;48.4418664;13.456074;-0.490208999;205.593667;0;1;77;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;48.4418678;3.82687569;8.34111881;2.24319601;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;48.4241932;0;45.3202546;3.82762094;8.34111844;2.24319601 +78;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.33635599;0;3.81964344;2.57240182;8.20897566;4.98716331;52079.3365;46.5530225;12.9313952;-0.557017302;197.577164;0;1;78;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;46.5530243;3.8196435;8.33635616;2.57240176;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;46.5329474;0;52.0793365;3.82026807;8.33635599;2.57240182 +79;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.33152709;0;3.81524403;2.84454927;8.20354771;4.98716331;58131.879;44.4377026;12.3438063;-0.616606473;188.599467;0;1;79;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;44.4377022;3.81524396;8.33152676;2.84454918;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;44.4154843;0;58.131879;3.81575195;8.33152709;2.84454927 +80;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.32672799;0;3.81148785;3.04959566;8.20198906;4.98716331;63553.1741;42.117539;11.6993164;-0.670083092;178.752387;0;1;80;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;42.1175385;3.81148791;8.32672787;3.04959559;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;42.093408;0;63.5531741;3.81179546;8.32672799;3.04959566 +81;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.3221029;0;3.80794972;3.14523603;8.20635956;4.98716331;67748.7308;39.6297462;11.0082628;-0.710827802;168.193867;0;1;81;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;39.6297455;3.80794978;8.32210255;3.14523602;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;39.6041466;0;67.7487308;3.8081046;8.3221029;3.14523603 +82;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.31774453;0;3.80666804;3.21249149;8.21159861;4.98716331;72062.1249;36.9964439;10.27679;-0.752433632;157.017785;0;1;82;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;36.9964447;3.80666804;8.31774426;3.21249151;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;36.9693326;0;72.0621249;3.80679269;8.31774453;3.21249149 +83;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.31364983;0;3.80569432;3.26540988;8.21688687;4.98716331;76825.555;34.2043924;9.50122012;-0.799220807;145.16795;0;1;83;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;34.2043915;3.80569434;8.31365013;3.26540995;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;34.1756059;0;76.825555;3.80579474;8.31364983;3.26540988 +84;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.30980883;0;3.80492735;3.30850588;8.22198779;4.98716331;82266.0555;31.2330265;8.67584069;-0.852497564;132.557081;0;1;84;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;31.2330265;3.80492735;8.30980873;3.30850577;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;31.2023188;0;82.2660555;3.80500828;8.30980883;3.30850588 +85;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.30620916;0;3.80431157;3.34378955;8.22682029;4.98716331;88633.9251;28.0538535;7.79273708;-0.915108055;119.064252;0;1;85;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;28.053854;3.80431151;8.30620956;3.34378958;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;28.0208876;0;88.6339251;3.80437683;8.30620916;3.34378955 +86;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.3028369;0;3.80380953;3.37155945;8.23140881;4.98716331;96240.6848;24.6278412;6.84106699;-0.990185125;104.523804;0;1;86;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;24.6278419;3.80380964;8.30283737;3.37155938;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;24.5921669;0;96.2406848;3.80386218;8.3028369;3.37155945 +87;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.29967734;0;3.80339495;3.39357797;8.23569383;4.98716331;105586.578;20.901047;5.80584639;-1.08275527;88.7067986;0;1;87;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;20.9010468;3.80339503;8.2996769;3.39357805;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;20.8620325;0;105.586578;3.80343745;8.29967734;3.39357797 +88;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.29671553;0;3.80304876;3.41181433;8.23960432;4.98716331;117451.666;16.7973304;4.66592512;-1.20065118;71.2900844;0;1;88;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;16.7973309;3.80304885;8.29671574;3.41181421;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;16.7540605;0;117.451666;3.8030831;8.29671553;3.41181433 +89;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.29393655;0;3.80276032;3.42821668;8.24306997;4.98716331;133112.236;12.2055225;3.39042291;-1.35601473;51.8018463;0;1;89;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;12.2055225;3.80276036;8.29393673;3.4282167;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;12.1566188;0;133.112236;3.80278806;8.29393655;3.42821668 +90;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.29131955;0;3.80254625;3.43909181;8.24635268;4.98716331;154567.437;6.95498602;1.93194056;-1.57087588;29.5178774;0;1;90;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;6.9549861;3.80254626;8.29131985;3.43909192;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;6.89834406;0;154.567437;3.80256865;8.29131955;3.43909181 +91;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.28884953;0;3.80236106;3.44814657;8.24927407;4.98716331;186087.335;0.766029348;0.21278593;-1.88599309;3.25112953;0;1;91;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0.766029358;3.80236101;8.28884983;3.44814658;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0.697994805;0;186.087335;3.80237916;8.28884953;3.44814657 +92;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.2865149;0;3.80219935;3.45718475;8.25176759;4.98716331;190993.041;0;0;0;0;0;1;92;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80219936;8.28651524;3.45718479;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;190.993041;3.80221399;8.2865149;3.45718475 +93;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.28430473;0;3.80205709;3.46258954;8.25408348;4.98716331;191295.499;0;0;0;0;0;1;93;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80205703;8.28430462;3.4625895;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;191.295499;3.80206895;8.28430473;3.46258954 +94;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.28220849;0;3.80193109;3.46799214;8.25604239;4.98716331;191597.781;0;0;0;0;0;1;94;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80193114;8.28220844;3.46799207;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;191.597781;3.80194071;8.28220849;3.46799214 +95;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.28021647;0;3.80181879;3.47159125;8.25778242;4.98716331;191799.125;0;0;0;0;0;1;95;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80181885;8.28021622;3.47159123;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;191.799125;3.8018266;8.28021647;3.47159125 +96;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.27831906;0;3.80171811;3.47519411;8.25922792;4.98716331;192000.656;0;0;0;0;0;1;96;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.801718;8.27831936;3.47519422;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.000656;3.80172447;8.27831906;3.47519411 +97;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.27650776;0;3.80162736;3.47879806;8.26040636;4.98716331;192202.224;0;0;0;0;0;1;97;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.8016274;8.27650738;3.47879815;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.202224;3.80163254;8.27650776;3.47879806 +98;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.27477477;0;3.80154516;3.48059648;8.26144918;4.98716331;192302.8;0;0;0;0;0;1;98;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80154514;8.27477455;3.48059654;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.3028;3.80154939;8.27477477;3.48059648 +99;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.27311263;0;3.80147034;3.48239845;8.26227323;4.98716331;192403.568;0;0;0;0;0;1;99;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80147028;8.2731123;3.48239851;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.403568;3.80147381;8.27311263;3.48239845 +100;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.27151449;0;3.80140196;3.48419844;8.26290037;4.98716331;192504.22;0;0;0;0;0;1;100;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80140185;8.27151489;3.48419833;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.50422;3.80140481;8.27151449;3.48419844 +101;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.26997446;0;3.80133921;3.48599692;8.26334907;4.98716331;192604.781;0;0;0;0;0;1;101;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80133915;8.26997471;3.48599696;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.604781;3.80134155;8.26997446;3.48599692 +102;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.26848675;0;3.80128141;3.48599692;8.26374332;4.98716331;192604.781;0;0;0;0;0;1;102;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80128145;8.26848698;3.48599696;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.604781;3.80128334;8.26848675;3.48599692 +103;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.26704582;0;3.80122799;3.48779621;8.26388809;4.98716331;192705.382;0;0;0;0;0;1;103;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80122805;8.26704597;3.48779631;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.705382;3.80122959;8.26704582;3.48779621 +104;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.2656477;0;3.80117846;3.48779621;8.26400805;4.98716331;192705.382;0;0;0;0;0;1;104;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80117846;8.26564789;3.48779631;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.705382;3.80117979;8.2656477;3.48779621 +105;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.2642885;0;3.8011324;3.48959482;8.26390298;4.98716331;192805.939;0;0;0;0;0;1;105;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80113244;8.2642889;3.48959494;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.805939;3.8011335;8.2642885;3.48959482 +106;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.26295301;0;3.80108944;3.48959482;8.26383213;4.98716331;192805.939;0;0;0;0;0;1;106;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80108953;8.2629528;3.48959494;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.805939;3.80109037;8.26295301;3.48959482 +107;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.26161848;0;3.80104928;3.48959482;8.26375781;4.98716331;192805.939;0;0;0;0;0;1;107;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80104923;8.26161861;3.48959494;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.805939;3.80105006;8.26161848;3.48959482 +108;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.26028371;0;3.80101164;3.48959482;8.2636835;4.98716331;192805.939;0;0;0;0;0;1;108;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80101156;8.26028347;3.48959494;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.805939;3.8010123;8.26028371;3.48959482 +109;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.25894875;0;3.80097629;3.48959482;8.26360919;4.98716331;192805.939;0;0;0;0;0;1;109;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80097628;8.25894833;3.48959494;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.805939;3.80097685;8.25894875;3.48959482 +110;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.25761363;0;3.80094302;3.49139194;8.26342875;4.98716331;192906.407;0;0;0;0;0;1;110;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.8009429;8.25761318;3.4913919;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.906407;3.80094349;8.25761363;3.49139194 +111;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.25627837;0;3.80091164;3.49139194;8.26335444;4.98716331;192906.407;0;0;0;0;0;1;111;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80091166;8.25627804;3.4913919;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.906407;3.80091205;8.25627837;3.49139194 +112;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.25494301;0;3.800882;3.49139194;8.26328012;4.98716331;192906.407;0;0;0;0;0;1;112;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.8008821;8.25494289;3.4913919;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.906407;3.80088235;8.25494301;3.49139194 +113;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.25360755;0;3.80085395;3.49139194;8.26320582;4.98716331;192906.407;0;0;0;0;0;1;113;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80085397;8.25360775;3.4913919;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.906407;3.80085425;8.25360755;3.49139194 +114;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.25227202;0;3.80082736;3.49139194;8.26313151;4.98716331;192906.407;0;0;0;0;0;1;114;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80082726;8.25227165;3.4913919;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.906407;3.80082762;8.25227202;3.49139194 +115;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.25093642;0;3.80080212;3.49139194;8.2630572;4.98716331;192906.407;0;0;0;0;0;1;115;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80080223;8.25093651;3.4913919;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.906407;3.80080235;8.25093642;3.49139194 +116;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.24960077;0;3.80077813;3.49139194;8.26298289;4.98716331;192906.407;0;0;0;0;0;1;116;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80077815;8.24960041;3.4913919;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.906407;3.80077833;8.24960077;3.49139194 +117;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.24826508;0;3.8007553;3.49139194;8.26290858;4.98716331;192906.407;0;0;0;0;0;1;117;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80075526;8.24826527;3.4913919;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.906407;3.80075547;8.24826508;3.49139194 +118;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.24692935;0;3.80073354;3.49139194;8.26283428;4.98716331;192906.407;0;0;0;0;0;1;118;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80073357;8.24692917;3.4913919;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.906407;3.80073369;8.24692935;3.49139194 +119;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.24559359;0;3.80071277;3.49139194;8.26275997;4.98716331;192906.407;0;0;0;0;0;1;119;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80071282;8.24559402;3.4913919;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.906407;3.80071291;8.24559359;3.49139194 +120;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.24425781;0;3.80069293;3.49139194;8.26268567;4.98716331;192906.407;0;0;0;0;0;1;120;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80069304;8.24425793;3.4913919;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.906407;3.80069306;8.24425781;3.49139194 +121;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.242922;0;3.80067396;3.49139194;8.26261136;4.98716331;192906.407;0;0;0;0;0;1;121;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80067396;8.24292183;3.4913919;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;192.906407;3.80067407;8.242922;3.49139194 +122;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.24158618;0;3.80065581;3.49318843;8.26243097;4.98716331;193006.833;0;0;0;0;0;1;122;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80065584;8.24158573;3.49318838;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;193.006833;3.8006559;8.24158618;3.49318843 +123;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.24025034;0;3.80063841;3.49318843;8.26235666;4.98716331;193006.833;0;0;0;0;0;1;123;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80063844;8.24025059;3.49318838;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;193.006833;3.8006385;8.24025034;3.49318843 +124;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.2389145;0;3.80062172;3.49318843;8.26228236;4.98716331;193006.833;0;0;0;0;0;1;124;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80062175;8.23891449;3.49318838;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;193.006833;3.8006218;8.2389145;3.49318843 +125;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.23757864;0;3.80060571;3.49318843;8.26220806;4.98716331;193006.833;0;0;0;0;0;1;125;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80060577;8.23757839;3.49318838;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;193.006833;3.80060577;8.23757864;3.49318843 +126;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.23624278;0;3.80059032;3.49318843;8.26213376;4.98716331;193006.833;0;0;0;0;0;1;126;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80059028;8.23624325;3.49318838;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;193.006833;3.80059038;8.23624278;3.49318843 +127;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.23490691;0;3.80057552;3.49318843;8.26205946;4.98716331;193006.833;0;0;0;0;0;1;127;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80057549;8.23490715;3.49318838;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;193.006833;3.80057558;8.23490691;3.49318843 +128;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.23357104;0;3.80056129;3.49318843;8.26198516;4.98716331;193006.833;0;0;0;0;0;1;128;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80056119;8.23357105;3.49318838;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;193.006833;3.80056134;8.23357104;3.49318843 +129;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.23223517;0;3.80054758;3.49318843;8.26191087;4.98716331;193006.833;0;0;0;0;0;1;129;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.8005476;8.23223495;3.49318838;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;193.006833;3.80054762;8.23223517;3.49318843 +130;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;3;5;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.2308993;0;3.80053437;3.49318843;8.26183657;4.98716331;193006.833;0;0;0;0;0;1;130;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;5;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;3.80053449;8.23089886;3.49318838;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;193.006833;3.80053441;8.2308993;3.49318843 +131;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0;0;0;0;0;1;2;0;0;0;0;0;2;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;3000;0;0;0;0;1;0.666666667;0;1;0;1;0;0;0;0;0;0;0;0;1;1;110;0;0;0;0;8.19059552;0;6.08846269;3.31961047;8.26176227;4.98716331;193006.833;0;0;0;0;0;1;131;0;0;0;0;0;21;0;0;0;1;1;0;0;0;0;3000;1;1;0;0;0;1;1;0;0;0;2;0;0;2;0;0;0;0;0;0;1;0;1;0;100;0;0;1;0;0;0;0;0;3000;0;0;6.08846283;8.19059563;3.31961036;1;1;1;0;1;1;0;1;1;0;1;1;0;1;1;1;1;1;0;0;193.006833;6.08597616;8.19059552;3.31961047 diff --git a/tools/vehicle_graph/examples/303e.script b/tools/vehicle_graph/examples/303e.script new file mode 100644 index 00000000..538644fc --- /dev/null +++ b/tools/vehicle_graph/examples/303e.script @@ -0,0 +1,33 @@ +# events for: -graph-run 303e.vhg -script 303e.script -state ready +# options: -state ready -every 100 +# start up from the cab, drive off with the resistors, run on position 20, then brake with the train brake to a stop +0.0 toggle bat +0.5 toggle pant_f +1.0 press ca_reset +1.3 release ca_reset +6.0 press ws_on +6.3 release ws_on +7.0 toggle conv_sw +11.0 toggle comp_sw +12.0 inc reverser +13.0 inc reverser +14.0 inc mainctrl +15.0 inc mainctrl +16.0 inc mainctrl +18.0 inc mainctrl +20.0 inc mainctrl +22.0 inc mainctrl +24.0 set mainctrl 8 +27.0 set mainctrl 10 +30.0 set mainctrl 12 +34.0 set mainctrl 14 +38.0 set mainctrl 16 +42.0 set mainctrl 18 +46.0 set mainctrl 20 +50.0 press ca_reset +50.3 release ca_reset +70.0 set mainctrl 0 +71.0 value brake 3 +100.0 press ca_reset +100.3 release ca_reset +130.0 value brake 0 diff --git a/tools/vehicle_graph/examples/303e.vhg b/tools/vehicle_graph/examples/303e.vhg new file mode 100644 index 00000000..d96dc526 --- /dev/null +++ b/tools/vehicle_graph/examples/303e.vhg @@ -0,0 +1,245 @@ +// electric locomotive ET22 with resistor starting, built from the 303E .fiz: the whole vehicle defined by the graph. +// series DC motors (drive.dc_series with the RList and MotorParamTable0 of the .fiz), LSt distributor, FV4a driver's +// brake valve, FD1 independent brake valve, vigilance device with cab signalling. +// the submodel names of the cab and the sound files are placeholders, to be replaced with the ones of the cab model +{ + "format": "maszyna-vehicle-graph", + "version": 2, + "vehicle": { "name": "303E" }, + "settings": { "low_voltage_simulation": false }, + "body": { + "category": "train", "mass": 83500, "mass_rotating": 16000, "vmax": 125, "power": 2000, "sand_capacity": 500, + "length": 16.315, "height": 4.343, "width": 2.903, "cx": 0.38, + "wheel_diameter": 1.25, "wheel_inertia": 2000, "track_gauge": 1.435, "axle_arrangement": "Bo'Bo'", "axle_distance": 3.05, "bogie_distance": 8.55, + "min_radius": 80, "bearing": "roll", + "coupler_type": "Screw", "coupler_kb": 9200, "coupler_dmax_b": 0.15, "coupler_fmax_b": 740, "coupler_kc": 4800, "coupler_dmax_c": 0.15, "coupler_fmax_c": 780, + "coupler_beta": 0.58, "coupler_allowed_flag": 103, "control_type": "303E", + "max_load": 52, "load_unit": "pieces", "load_accepted": "pantstate", "load_speed": 1, "unload_speed": 0.1, + "collector_max_voltage": 3600, "collector_max_current": 1250, "collector_min_h": 0.48, "collector_max_h": 2.5, "collector_csw": 1.4, + "collector_min_voltage": 1900, "collector_layout": 3 + }, + "profiles": { "brake_pipe": 1, "heating": 1 }, + "nodes": [ + // cab controls + { "id": "bat", "type": "cab.switch", "title": "Battery", "params": { "behaviour": "bistable", "submodel": "battery_sw", "command": "batterytoggle" } }, + { "id": "pant_f", "type": "cab.switch", "title": "Front pantograph", "params": { "behaviour": "bistable", "submodel": "pantfront_sw", "command": "pantographtogglefront" } }, + { "id": "pant_r", "type": "cab.switch", "title": "Rear pantograph", "params": { "behaviour": "bistable", "submodel": "pantrear_sw", "command": "pantographtogglerear" } }, + { "id": "ws_on", "type": "cab.switch", "title": "Line breaker on", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "main_on_bt", "command": "linebreakerclose" } }, + { "id": "ws_off", "type": "cab.switch", "title": "Line breaker off", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "main_off_bt", "command": "linebreakeropen" } }, + { "id": "conv_sw", "type": "cab.switch", "title": "Converter", "params": { "behaviour": "bistable", "submodel": "converter_sw", "command": "convertertoggle" } }, + { "id": "comp_sw", "type": "cab.switch", "title": "Compressor", "params": { "behaviour": "bistable", "submodel": "compressor_sw", "command": "compressortoggle" } }, + { "id": "heat_sw", "type": "cab.switch", "title": "Train heating", "params": { "behaviour": "bistable", "submodel": "trainheating_sw", "command": "heatingtoggle" } }, + { "id": "high_current", "type": "cab.switch", "title": "High starting current", "params": { "behaviour": "bistable", "submodel": "maxcurrent_sw", "command": "motoroverloadrelaythresholdtoggle" } }, + { "id": "relay_reset", "type": "cab.switch", "title": "Overload relay reset", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "fuse_bt", "command": "motoroverloadrelayreset" } }, + { "id": "reverser", "type": "cab.switch", "title": "Reverser", "params": { "behaviour": "multi", "positions": 3, "min": -1, "max": 1, "default": 0, "labels": "back;0;forward", "step": 45, "offset": -45, "submodel": "dirkey", "key_inc": "reverserincrease", "key_dec": "reverserdecrease" } }, + { "id": "mainctrl", "type": "cab.switch", "title": "Master controller", "params": { "behaviour": "multi", "positions": 44, "min": 0, "max": 43, "default": 0, "step": 6, "submodel": "mainctrl", "key_inc": "mastercontrollerincrease", "key_dec": "mastercontrollerdecrease" } }, + { "id": "shunt", "type": "cab.switch", "title": "Field shunting", "params": { "behaviour": "multi", "positions": 7, "min": 0, "max": 6, "default": 0, "step": 15, "submodel": "scndctrl", "key_inc": "secondcontrollerincrease", "key_dec": "secondcontrollerdecrease" } }, + { "id": "brake", "type": "cab.switch", "title": "Driver's brake valve (FV4a)", "params": { "behaviour": "multi", "positions": 9, "min": -2, "max": 6, "default": 0, "labels": "cut-off;filling;running;1;2;3;4;full;emergency", "step": 12, "offset": -24, "submodel": "brakectrl", "key_inc": "trainbrakeincrease", "key_dec": "trainbrakedecrease" } }, + { "id": "local", "type": "cab.switch", "title": "Independent brake (FD1)", "params": { "behaviour": "multi", "positions": 11, "min": 0, "max": 1, "default": 0, "step": 9, "submodel": "localbrake", "key_inc": "independentbrakeincrease", "key_dec": "independentbrakedecrease" } }, + { "id": "ca_reset", "type": "cab.switch", "title": "Vigilance reset", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "security_reset_bt", "command": "alerteracknowledge" } }, + { "id": "sand_bt", "type": "cab.switch", "title": "Sander", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "sand_bt", "command": "sandboxactivate" } }, + { "id": "horn_lo", "type": "cab.switch", "title": "Horn low", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "hornlow_bt", "command": "hornlowactivate" } }, + { "id": "horn_hi", "type": "cab.switch", "title": "Horn high", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "hornhigh_bt", "command": "hornhighactivate" } }, + { "id": "lights_sw", "type": "cab.switch", "title": "Head lights", "params": { "behaviour": "bistable", "submodel": "upperlight_sw", "command": "headlighttoggleupper" } }, + + // power circuit + { "id": "pant_f_up", "type": "logic.and", "title": "Front pantograph valve" }, + { "id": "pant_r_up", "type": "logic.and", "title": "Rear pantograph valve" }, + { "id": "pf", "type": "el.pantograph", "title": "Front pantograph", "params": { "index": 0, "raise_time": 4, "lower_time": 1, "min_pressure": 3.5 } }, + { "id": "pr", "type": "el.pantograph", "title": "Rear pantograph", "params": { "index": 1, "raise_time": 4, "lower_time": 1, "min_pressure": 3.5 } }, + { "id": "hv", "type": "el.junction", "title": "Roof line" }, + { "id": "ws_close", "type": "logic.and", "title": "Closing the line breaker" }, + { "id": "ws", "type": "el.hsb", "title": "Line breaker", "params": { "trip_current": 2500, "close_time": 0.2, "min_voltage": 1900 } }, + { "id": "dir_set", "type": "logic.compare", "title": "Direction selected", "params": { "op": "ne", "value": 0 } }, + { "id": "power_req", "type": "logic.compare", "title": "Controller above zero", "params": { "op": "gt", "value": 0 } }, + { "id": "no_overload", "type": "logic.not", "title": "Overload relay not tripped" }, + { "id": "line_req", "type": "logic.and", "title": "Line contactors requested" }, + { "id": "line_req2", "type": "logic.and", "title": "Line contactors allowed" }, + { "id": "line", "type": "el.contactor", "title": "Line contactors", "params": { "delay": 0.1 } }, + { "id": "motors", "type": "drive.dc_series", "title": "Traction motors (4 in series)", "params": { + "winding_res": 0.14, "circuit_res": 0.5, "imax_lo": 600, "imax_hi": 750, "imin_lo": 350, "imin_hi": 470, "volt": 1500, "trip_delay": 0.05, + "mpt": [ + [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] + ], + "rlist": [ + [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.78, 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, 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.1, 2, 2, 0], [43, 0, 2, 2, 0] + ] } }, + { "id": "gear", "type": "drive.gear", "title": "Gear 18:79", "params": { "ratio": 4.388888889, "eff": 1.0 } }, + { "id": "axles", "type": "drive.axle", "title": "Driven axles", "params": { "powered_axles": 4 } }, + { "id": "conv", "type": "el.converter", "title": "Motor generator", "params": { "power": 40000, "start_time": 3, "min_voltage": 1900, "out_voltage": 400 } }, + { "id": "batt", "type": "el.battery", "title": "Battery", "params": { "voltage": 110, "capacity": 100, "min_voltage": 80 } }, + { "id": "heat", "type": "if.heat", "title": "Train heating", "params": { "side": "rear" } }, + + // air + { "id": "comp_on", "type": "logic.and", "title": "Compressor running" }, + { "id": "comp", "type": "pn.compressor", "title": "Compressor", "params": { "flow": 0.0566, "max_pressure": 8.5, "min_voltage": 300, "power": 15000 } }, + { "id": "gov", "type": "pn.governor", "title": "Pressure governor", "params": { "min": 7.5, "max": 8.5 } }, + { "id": "res", "type": "pn.reservoir", "title": "Main reservoir", "params": { "volume": 0.828 } }, + { "id": "fv4a", "type": "br.handle", "title": "FV4a", "params": { "type": "FV4a" } }, + { "id": "pipe", "type": "br.pipe", "title": "Brake pipe" }, + { "id": "fd1", "type": "br.local_handle", "title": "FD1", "params": { "type": "FD1" } }, + { "id": "dist", "type": "br.distributor", "title": "LSt distributor", "params": { + "type": "LSt", "mode": "GPR", "max_bp": 4.4, "max_asbp": 1.1, "cyl_count": 8, "cyl_radius": 0.1143, "cyl_dist": 0.05, "volume": 278, "rm": 0.71, + "delay1": 15, "delay2": 7, "delay3": 39, "delay4": 17, "asb": "manual" } }, + { "id": "rig", "type": "br.rigging", "title": "Brake rigging", "params": { "bcm": 5.18, "bcs": 1, "bsa": 0.5, "bre": 0.85, "nbpa": 4, "material": "P10-Bgu" } }, + { "id": "pg_front", "type": "if.pg", "title": "Brake pipe, front", "params": { "side": "front" } }, + { "id": "pg_rear", "type": "if.pg", "title": "Brake pipe, rear", "params": { "side": "rear" } }, + + // vehicle + { "id": "vs", "type": "veh.state", "title": "Motion" }, + { "id": "ca", "type": "sec.ca", "title": "Vigilance device (CA/SHP)", "params": { + "aware": true, "cab_signal": true, "aware_delay": 60, "sound_delay": 3, "emergency_delay": 4, "min_speed": 10, "max_hold_time": 1.0, "radio_stop": true } }, + { "id": "sand", "type": "veh.sander", "title": "Sander" }, + { "id": "pc1", "type": "logic.const", "title": "Pc1: three white lights", "params": { "value": 21 } }, + { "id": "pc_front", "type": "logic.math", "title": "Front lights when switched on", "params": { "op": "mul" } }, + { "id": "lights", "type": "veh.lights", "title": "Lights" }, + + // indicators + { "id": "ws_open", "type": "logic.not", "title": "Line breaker open" }, + { "id": "ws_lamp", "type": "logic.and", "title": "Line breaker lamp powered" }, + { "id": "line_open", "type": "logic.not", "title": "Line contactors open" }, + { "id": "line_lamp", "type": "logic.and", "title": "Line contactors lamp powered" }, + { "id": "ovl_lamp", "type": "logic.and", "title": "Overload lamp powered" }, + { "id": "conv_off", "type": "logic.not", "title": "Converter stopped" }, + { "id": "conv_lamp", "type": "logic.and", "title": "Converter lamp powered" }, + { "id": "slip_lamp", "type": "logic.and", "title": "Wheel slip lamp powered" }, + { "id": "lamp_ws", "type": "fb.lamp", "title": "Line breaker", "params": { "color": "red", "submodel": "i-mainbreaker" } }, + { "id": "lamp_line", "type": "fb.lamp", "title": "Line contactors", "params": { "color": "green", "submodel": "i-contactors" } }, + { "id": "lamp_ovl", "type": "fb.lamp", "title": "Motor overload", "params": { "color": "red", "submodel": "i-motor_ovld" } }, + { "id": "lamp_conv", "type": "fb.lamp", "title": "Converter", "params": { "color": "yellow", "submodel": "i-converter" } }, + { "id": "lamp_slip", "type": "fb.lamp", "title": "Wheel slip", "params": { "color": "yellow", "submodel": "i-slippery" } }, + { "id": "lamp_ca", "type": "fb.lamp", "title": "Vigilance", "params": { "color": "white", "submodel": "i-security_aware" } }, + { "id": "lamp_shp", "type": "fb.lamp", "title": "Cab signal", "params": { "color": "white", "submodel": "i-security_cabsignal" } }, + { "id": "hv_volts", "type": "logic.math", "title": "Voltage of the roof line", "params": { "op": "max" } }, + { "id": "g_hv", "type": "fb.gauge", "title": "High voltage", "params": { "min": 0, "max": 4000, "unit": "V", "angle_min": 0, "angle_max": 270, "submodel": "hvoltage" } }, + { "id": "g_i", "type": "fb.gauge", "title": "Motor current", "params": { "min": 0, "max": 1000, "unit": "A", "angle_min": 0, "angle_max": 270, "submodel": "hvcurrent1" } }, + { "id": "g_v", "type": "fb.gauge", "title": "Speedometer", "params": { "min": 0, "max": 150, "unit": "km/h", "angle_min": 0, "angle_max": 300, "submodel": "tachometer" } }, + { "id": "g_pipes", "type": "fb.manometer", "title": "Brake pipe and main reservoir", "params": { "min": 0, "max": 10, "submodel": "pipepress", "submodel2": "compressor" } }, + { "id": "g_bc", "type": "fb.gauge", "title": "Brake cylinders", "params": { "min": 0, "max": 10, "unit": "bar", "angle_min": 0, "angle_max": 270, "submodel": "cylinder" } }, + { "id": "snd_conv", "type": "fb.sound", "title": "Motor generator", "params": { "file": "et22-przetwornica.wav", "placement": "engine", "loop": true } }, + { "id": "snd_comp", "type": "fb.sound", "title": "Compressor", "params": { "file": "et22-sprezarka.wav", "placement": "engine", "loop": true } }, + { "id": "snd_buzzer", "type": "fb.sound", "title": "Vigilance buzzer", "params": { "file": "buzzer.wav", "placement": "internal", "loop": true } }, + { "id": "snd_horn_lo", "type": "fb.sound", "title": "Horn low", "params": { "file": "horn1.wav", "placement": "external", "range": 1000, "loop": true } }, + { "id": "snd_horn_hi", "type": "fb.sound", "title": "Horn high", "params": { "file": "horn2.wav", "placement": "external", "range": 1000, "loop": true } }, + { "id": "snd_ws", "type": "fb.sound", "title": "Line breaker", "params": { "file": "wylacznik.wav", "placement": "engine", "loop": false } } + ], + "connections": [ + { "from": "bat.pos", "to": "batt.on" }, + { "from": "conv.aux", "to": "batt.charge" }, + + { "from": "bat.pos", "to": "pant_f_up.a" }, + { "from": "pant_f.pos", "to": "pant_f_up.b" }, + { "from": "bat.pos", "to": "pant_r_up.a" }, + { "from": "pant_r.pos", "to": "pant_r_up.b" }, + { "from": "pant_f_up.q", "to": "pf.raise" }, + { "from": "pant_r_up.q", "to": "pr.raise" }, + { "from": "res.out", "to": "pf.air" }, + { "from": "res.out", "to": "pr.air" }, + { "from": "pf.wn", "to": "hv.a" }, + { "from": "pr.wn", "to": "hv.b" }, + { "from": "hv.out", "to": "ws.in" }, + { "from": "ws_on.pos", "to": "ws_close.a" }, + { "from": "bat.pos", "to": "ws_close.b" }, + { "from": "ws_close.q", "to": "ws.close" }, + { "from": "ws_off.pos", "to": "ws.open" }, + { "from": "bat.pos", "to": "ws.hold" }, + + { "from": "reverser.pos", "to": "dir_set.a" }, + { "from": "mainctrl.pos", "to": "power_req.a" }, + { "from": "motors.overload", "to": "no_overload.a" }, + { "from": "dir_set.q", "to": "line_req.a" }, + { "from": "power_req.q", "to": "line_req.b" }, + { "from": "line_req.q", "to": "line_req2.a" }, + { "from": "no_overload.q", "to": "line_req2.b" }, + { "from": "line_req2.q", "to": "line.on" }, + { "from": "ws.out", "to": "line.in" }, + { "from": "line.out", "to": "motors.wn" }, + { "from": "mainctrl.index", "to": "motors.position" }, + { "from": "shunt.index", "to": "motors.shunt" }, + { "from": "reverser.pos", "to": "motors.dir" }, + { "from": "high_current.pos", "to": "motors.high" }, + { "from": "relay_reset.pos", "to": "motors.reset" }, + { "from": "motors.shaft", "to": "gear.in" }, + { "from": "gear.out", "to": "axles.shaft" }, + + { "from": "ws.out", "to": "conv.wn" }, + { "from": "conv_sw.pos", "to": "conv.on" }, + { "from": "ws.out", "to": "heat.wn" }, + { "from": "heat_sw.pos", "to": "heat.on" }, + + { "from": "conv.aux", "to": "comp.power" }, + { "from": "comp_sw.pos", "to": "comp_on.a" }, + { "from": "gov.need", "to": "comp_on.b" }, + { "from": "comp_on.q", "to": "comp.on" }, + { "from": "comp.air", "to": "res.in" }, + { "from": "res.p", "to": "gov.p" }, + { "from": "res.out", "to": "fv4a.zg" }, + { "from": "brake.pos", "to": "fv4a.pos" }, + { "from": "fv4a.pg", "to": "pipe.in" }, + { "from": "ca.brake", "to": "pipe.vent" }, + { "from": "res.out", "to": "fd1.zg" }, + { "from": "local.pos", "to": "fd1.pos" }, + { "from": "pipe.dist", "to": "dist.pg" }, + { "from": "res.pz", "to": "dist.feed" }, + { "from": "fd1.bc", "to": "dist.local" }, + { "from": "dist.bc", "to": "rig.bc" }, + { "from": "pipe.couplers", "to": "pg_front.pg" }, + { "from": "pipe.couplers", "to": "pg_rear.pg" }, + + { "from": "vs.v", "to": "ca.v" }, + { "from": "batt.ok", "to": "ca.power" }, + { "from": "ca_reset.pos", "to": "ca.reset" }, + { "from": "sand_bt.pos", "to": "sand.on" }, + { "from": "pc1.q", "to": "pc_front.a" }, + { "from": "lights_sw.pos", "to": "pc_front.b" }, + { "from": "pc_front.q", "to": "lights.front" }, + + { "from": "ws.closed", "to": "ws_open.a" }, + { "from": "ws_open.q", "to": "ws_lamp.a" }, + { "from": "batt.ok", "to": "ws_lamp.b" }, + { "from": "ws_lamp.q", "to": "lamp_ws.on" }, + { "from": "line.closed", "to": "line_open.a" }, + { "from": "line_open.q", "to": "line_lamp.a" }, + { "from": "batt.ok", "to": "line_lamp.b" }, + { "from": "line_lamp.q", "to": "lamp_line.on" }, + { "from": "motors.overload", "to": "ovl_lamp.a" }, + { "from": "batt.ok", "to": "ovl_lamp.b" }, + { "from": "ovl_lamp.q", "to": "lamp_ovl.on" }, + { "from": "conv.run", "to": "conv_off.a" }, + { "from": "conv_off.q", "to": "conv_lamp.a" }, + { "from": "batt.ok", "to": "conv_lamp.b" }, + { "from": "conv_lamp.q", "to": "lamp_conv.on" }, + { "from": "axles.slip", "to": "slip_lamp.a" }, + { "from": "batt.ok", "to": "slip_lamp.b" }, + { "from": "slip_lamp.q", "to": "lamp_slip.on" }, + { "from": "ca.lamp", "to": "lamp_ca.on" }, + { "from": "ca.shp_lamp", "to": "lamp_shp.on" }, + { "from": "pf.u", "to": "hv_volts.a" }, + { "from": "pr.u", "to": "hv_volts.b" }, + { "from": "hv_volts.q", "to": "g_hv.value" }, + { "from": "motors.i", "to": "g_i.value" }, + { "from": "vs.v", "to": "g_v.value" }, + { "from": "pipe.p", "to": "g_pipes.black" }, + { "from": "res.p", "to": "g_pipes.red" }, + { "from": "dist.bc", "to": "g_bc.value" }, + { "from": "conv.run", "to": "snd_conv.play" }, + { "from": "comp.run", "to": "snd_comp.play" }, + { "from": "ca.buzzer", "to": "snd_buzzer.play" }, + { "from": "horn_lo.pos", "to": "snd_horn_lo.play" }, + { "from": "horn_hi.pos", "to": "snd_horn_hi.play" }, + { "from": "ws.closed", "to": "snd_ws.play" } + ] +} diff --git a/tools/vehicle_graph/examples/vectron-logic.expected.csv b/tools/vehicle_graph/examples/vectron-logic.expected.csv index a46e05ed..511493e6 100644 --- a/tools/vehicle_graph/examples/vectron-logic.expected.csv +++ b/tools/vehicle_graph/examples/vectron-logic.expected.csv @@ -1,253 +1,253 @@ -time;bat.pos;bat.index;pant_sel.pos;pant_sel.index;pant_raise.pos;pant_raise.index;pant_lower.pos;pant_lower.index;main_sw.pos;main_sw.index;reverser.pos;reverser.index;lever.pos;lever.index;pant_cmd.q;pant_cmd_on.q;pant_cmd_off.q;no_lower.q;pant_up.q;hv_nominal.q;hv.q;hv_ok.q;ms_on_req.q;ms_off_req.q;ms_init.q;ms_close.q;ms_lost.q;ms_open.q;ms.q;ms_lamp_on.q;zero.q;minus_one.q;lever_pos.q;lever_neg.q;lever_brake.q;dir_set.q;drive_ok.q;traction.q;ed_brake.q;pant_moving.q;pant_not_up.q;snd_base.q;snd_gain.q;bat:position;pant_sel:position;pant_raise:position;pant_lower:position;main_sw:position;reverser:position;lever:position;pant_cmd:state;pant_up:elapsed;ms_init:elapsed;ms:state;lamp_ms:lit;lamp_pant:lit;v_hv:value;v_demand:value;snd_inverter:playing;snd_inverter:volume;snd_inverter:pitch -0.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1 -0.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1 -0.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1 -0.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1 -0.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1 -0.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1 -0.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1 -0.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1 -0.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1 -1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1 -1.1;1;1;1;1;1;1;0;0;0;1;0;1;0;15;1;1;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;1;0;1;1;15;1;0.09;0;0;1;1;0;0;0;0.300000012;1 -1.2;1;1;1;1;1;1;0;0;0;1;0;1;0;15;1;1;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;1;0;1;1;15;1;0.19;0;0;1;1;0;0;0;0.300000012;1 -1.3;1;1;1;1;1;1;0;0;0;1;0;1;0;15;1;1;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;1;0;1;1;15;1;0.29;0;0;1;1;0;0;0;0.300000012;1 -1.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.39;0;0;1;1;0;0;0;0.300000012;1 -1.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.49;0;0;1;0;0;0;0;0.300000012;1 -1.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.59;0;0;1;0;0;0;0;0.300000012;1 -1.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.69;0;0;1;0;0;0;0;0.300000012;1 -1.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.79;0;0;1;0;0;0;0;0.300000012;1 -1.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.89;0;0;1;0;0;0;0;0.300000012;1 -2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.99;0;0;1;1;0;0;0;0.300000012;1 -2.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.09;0;0;1;1;0;0;0;0.300000012;1 -2.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.19;0;0;1;1;0;0;0;0.300000012;1 -2.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.29;0;0;1;1;0;0;0;0.300000012;1 -2.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.39;0;0;1;1;0;0;0;0.300000012;1 -2.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.49;0;0;1;1;0;0;0;0.300000012;1 -2.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.59;0;0;1;0;0;0;0;0.300000012;1 -2.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.69;0;0;1;0;0;0;0;0.300000012;1 -2.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.79;0;0;1;0;0;0;0;0.300000012;1 -2.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.89;0;0;1;0;0;0;0;0.300000012;1 -3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.99;0;0;1;0;0;0;0;0.300000012;1 -3.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.09;0;0;1;1;0;0;0;0.300000012;1 -3.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.19;0;0;1;1;0;0;0;0.300000012;1 -3.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.29;0;0;1;1;0;0;0;0.300000012;1 -3.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.39;0;0;1;1;0;0;0;0.300000012;1 -3.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.49;0;0;1;1;0;0;0;0.300000012;1 -3.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.59;0;0;1;0;0;0;0;0.300000012;1 -3.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.69;0;0;1;0;0;0;0;0.300000012;1 -3.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.79;0;0;1;0;0;0;0;0.300000012;1 -3.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.89;0;0;1;0;0;0;0;0.300000012;1 -4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.99;0;0;1;0;0;0;0;0.300000012;1 -4.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.09;0;0;1;1;0;0;0;0.300000012;1 -4.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.19;0;0;1;1;0;0;0;0.300000012;1 -4.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.29;0;0;1;1;0;0;0;0.300000012;1 -4.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.39;0;0;1;1;0;0;0;0.300000012;1 -4.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.49;0;0;1;1;0;0;0;0.300000012;1 -4.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.59;0;0;1;0;0;0;0;0.300000012;1 -4.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.69;0;0;1;0;0;0;0;0.300000012;1 -4.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.79;0;0;1;0;0;0;0;0.300000012;1 -4.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.89;0;0;1;0;0;0;0;0.300000012;1 -5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.99;0;0;1;0;0;0;0;0.300000012;1 -5.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.09;0;0;1;1;0;0;0;0.300000012;1 -5.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.19;0;0;1;1;0;0;0;0.300000012;1 -5.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.29;0;0;1;1;0;0;0;0.300000012;1 -5.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.39;0;0;1;1;0;0;0;0.300000012;1 -5.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.49;0;0;1;1;0;0;0;0.300000012;1 -5.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.59;0;0;1;0;0;0;0;0.300000012;1 -5.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.69;0;0;1;0;0;0;0;0.300000012;1 -5.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.79;0;0;1;0;0;0;0;0.300000012;1 -5.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.89;0;0;1;0;0;0;0;0.300000012;1 -6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.99;0;0;1;0;0;0;0;0.300000012;1 -6.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.08;0;1;0;3000;0;0;0.300000012;1 -6.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.18;0;1;0;3000;0;0;0.300000012;1 -6.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.28;0;1;0;3000;0;0;0.300000012;1 -6.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.38;0;1;0;3000;0;0;0.300000012;1 -6.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.48;0;1;0;3000;0;0;0.300000012;1 -6.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.58;0;1;0;3000;0;0;0.300000012;1 -6.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.68;0;1;0;3000;0;0;0.300000012;1 -6.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.78;0;1;0;3000;0;0;0.300000012;1 -6.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.88;0;1;0;3000;0;0;0.300000012;1 -7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.98;0;1;0;3000;0;0;0.300000012;1 -7.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.08;0;1;0;3000;0;0;0.300000012;1 -7.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.18;0;1;0;3000;0;0;0.300000012;1 -7.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.28;0;1;0;3000;0;0;0.300000012;1 -7.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.38;0;1;0;3000;0;0;0.300000012;1 -7.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.48;0;1;0;3000;0;0;0.300000012;1 -7.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.58;0;1;0;3000;0;0;0.300000012;1 -7.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.68;0;1;0;3000;0;0;0.300000012;1 -7.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.78;0;1;0;3000;0;0;0.300000012;1 -7.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.88;0;1;0;3000;0;0;0.300000012;1 -8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.98;0;1;0;3000;0;0;0.300000012;1 -8.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.08;0;1;0;3000;0;0;0.300000012;1 -8.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.18;0;1;0;3000;0;0;0.300000012;1 -8.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.28;0;1;0;3000;0;0;0.300000012;1 -8.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.38;0;1;0;3000;0;0;0.300000012;1 -8.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.48;0;1;0;3000;0;0;0.300000012;1 -8.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.58;0;1;0;3000;0;0;0.300000012;1 -8.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.68;0;1;0;3000;0;0;0.300000012;1 -8.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.78;0;1;0;3000;0;0;0.300000012;1 -8.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.88;0;1;0;3000;0;0;0.300000012;1 -9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.98;0;1;0;3000;0;0;0.300000012;1 -9.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.08;0;1;0;3000;0;0;0.300000012;1 -9.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.18;0;1;0;3000;0;0;0.300000012;1 -9.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.28;0;1;0;3000;0;0;0.300000012;1 -9.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.38;0;1;0;3000;0;0;0.300000012;1 -9.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.48;0;1;0;3000;0;0;0.300000012;1 -9.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.58;0;1;0;3000;0;0;0.300000012;1 -9.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.68;0;1;0;3000;0;0;0.300000012;1 -9.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.78;0;1;0;3000;0;0;0.300000012;1 -9.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.88;0;1;0;3000;0;0;0.300000012;1 -10;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.98;0;1;0;3000;0;0;0.300000012;1 -10.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.08;0;1;0;3000;0;0;0.300000012;1 -10.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.18;0;1;0;3000;0;0;0.300000012;1 -10.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.28;0;1;0;3000;0;0;0.300000012;1 -10.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.38;0;1;0;3000;0;0;0.300000012;1 -10.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.48;0;1;0;3000;0;0;0.300000012;1 -10.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.58;0;1;0;3000;0;0;0.300000012;1 -10.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.68;0;1;0;3000;0;0;0.300000012;1 -10.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.78;0;1;0;3000;0;0;0.300000012;1 -10.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.88;0;1;0;3000;0;0;0.300000012;1 -11;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.98;0;1;0;3000;0;0;0.300000012;1 -11.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.08;0;1;0;3000;0;0;0.300000012;1 -11.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.18;0;1;0;3000;0;0;0.300000012;1 -11.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.28;0;1;0;3000;0;0;0.300000012;1 -11.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.38;0;1;0;3000;0;0;0.300000012;1 -11.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.48;0;1;0;3000;0;0;0.300000012;1 -11.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.58;0;1;0;3000;0;0;0.300000012;1 -11.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.68;0;1;0;3000;0;0;0.300000012;1 -11.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.78;0;1;0;3000;0;0;0.300000012;1 -11.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.88;0;1;0;3000;0;0;0.300000012;1 -12;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.98;0;1;0;3000;0;0;0.300000012;1 -12.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.08;0;1;0;3000;0;0;0.300000012;1 -12.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.18;0;1;0;3000;0;0;0.300000012;1 -12.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.28;0;1;0;3000;0;0;0.300000012;1 -12.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.38;0;1;0;3000;0;0;0.300000012;1 -12.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.48;0;1;0;3000;0;0;0.300000012;1 -12.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.58;0;1;0;3000;0;0;0.300000012;1 -12.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.68;0;1;0;3000;0;0;0.300000012;1 -12.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.78;0;1;0;3000;0;0;0.300000012;1 -12.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.88;0;1;0;3000;0;0;0.300000012;1 -13;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.98;0;1;0;3000;0;0;0.300000012;1 -13.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.08;0;1;0;3000;0;0;0.300000012;1 -13.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.18;0;1;0;3000;0;0;0.300000012;1 -13.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.28;0;1;0;3000;0;0;0.300000012;1 -13.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.38;0;1;0;3000;0;0;0.300000012;1 -13.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.48;0;1;0;3000;0;0;0.300000012;1 -13.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.58;0;1;0;3000;0;0;0.300000012;1 -13.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.68;0;1;0;3000;0;0;0.300000012;1 -13.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.78;0;1;0;3000;0;0;0.300000012;1 -13.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.88;0;1;0;3000;0;0;0.300000012;1 -14;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.98;0;1;0;3000;0;0;0.300000012;1 -14.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.08;0;1;0;3000;0;0;0.300000012;1 -14.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.18;0;1;0;3000;0;0;0.300000012;1 -14.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.28;0;1;0;3000;0;0;0.300000012;1 -14.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.38;0;1;0;3000;0;0;0.300000012;1 -14.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.48;0;1;0;3000;0;0;0.300000012;1 -14.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.58;0;1;0;3000;0;0;0.300000012;1 -14.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.68;0;1;0;3000;0;0;0.300000012;1 -14.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.78;0;1;0;3000;0;0;0.300000012;1 -14.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.88;0;1;0;3000;0;0;0.300000012;1 -15;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.98;0;1;0;3000;0;0;0.300000012;1 -15.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.08;0;1;0;3000;0;0;0.300000012;1 -15.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.18;0;1;0;3000;0;0;0.300000012;1 -15.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.28;0;1;0;3000;0;0;0.300000012;1 -15.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.38;0;1;0;3000;0;0;0.300000012;1 -15.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.48;0;1;0;3000;0;0;0.300000012;1 -15.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.58;0;1;0;3000;0;0;0.300000012;1 -15.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.68;0;1;0;3000;0;0;0.300000012;1 -15.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.78;0;1;0;3000;0;0;0.300000012;1 -15.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.88;0;1;0;3000;0;0;0.300000012;1 -16;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.98;0;1;0;3000;0;0;0.300000012;1 -16.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1 -16.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1 -16.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1 -16.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1 -16.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1 -16.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1 -16.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1 -16.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1 -16.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1 -17;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1 -17.1;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -17.2;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -17.3;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -17.4;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -17.5;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -17.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -17.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -17.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -17.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -18;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -18.1;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -18.2;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -18.3;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -18.4;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -18.5;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -18.6;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -18.7;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -18.8;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -18.9;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -19;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1 -19.1;1;1;1;1;0;0;0;0;0;1;1;2;0.0666666667;16;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.0666666667;-0.0666666667;0;1;1;0.0666666667;0;0;0;0.3;0.366666667;1;1;0;0;1;2;16;1;5;10;1;0;0;3000;0.0666666701;1;0.366666675;1 -19.2;1;1;1;1;0;0;0;0;0;1;1;2;0.133333333;17;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.133333333;-0.133333333;0;1;1;0.133333333;0;0;0;0.3;0.433333333;1;1;0;0;1;2;17;1;5;10;1;0;0;3000;0.13333334;1;0.433333337;1 -19.3;1;1;1;1;0;0;0;0;0;1;1;2;0.2;18;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.2;-0.2;0;1;1;0.2;0;0;0;0.3;0.5;1;1;0;0;1;2;18;1;5;10;1;0;0;3000;0.200000003;1;0.5;1 -19.4;1;1;1;1;0;0;0;0;0;1;1;2;0.266666667;19;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.266666667;-0.266666667;0;1;1;0.266666667;0;0;0;0.3;0.566666667;1;1;0;0;1;2;19;1;5;10;1;0;0;3000;0.266666681;1;0.566666663;1 -19.5;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -19.6;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -19.7;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -19.8;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -19.9;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -20;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -20.1;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -20.2;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -20.3;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -20.4;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -20.5;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -20.6;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -20.7;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -20.8;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -20.9;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -21;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1 -21.1;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -21.2;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -21.3;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -21.4;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -21.5;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -21.6;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -21.7;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -21.8;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -21.9;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -22;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -22.1;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -22.2;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -22.3;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -22.4;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -22.5;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -22.6;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -22.7;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -22.8;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -22.9;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -23;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1 -23.1;1;1;1;1;0;0;0;0;-1;0;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;1;1;0;0;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;0;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1 -23.2;1;1;1;1;0;0;0;0;-1;0;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;1;1;0;0;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;0;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1 -23.3;1;1;1;1;0;0;0;0;-1;0;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;1;1;0;0;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;0;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1 -23.4;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1 -23.5;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1 -23.6;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1 -23.7;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1 -23.8;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1 -23.9;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1 -24;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1 -24.1;1;1;1;1;0;0;1;1;0;1;1;2;-0.333333333;10;0;0;1;0;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;1;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1 -24.2;1;1;1;1;0;0;1;1;0;1;1;2;-0.333333333;10;0;0;1;0;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;1;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1 -24.3;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1 -24.4;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1 -24.5;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1 -24.6;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1 -24.7;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1 -24.8;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1 -24.9;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1 -25;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1 -25.1;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1 -25.2;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1 +time;bat.pos;bat.index;pant_sel.pos;pant_sel.index;pant_raise.pos;pant_raise.index;pant_lower.pos;pant_lower.index;main_sw.pos;main_sw.index;reverser.pos;reverser.index;lever.pos;lever.index;pant_cmd.q;pant_cmd_on.q;pant_cmd_off.q;no_lower.q;pant_up.q;hv_nominal.q;hv.q;hv_ok.q;ms_on_req.q;ms_off_req.q;ms_init.q;ms_close.q;ms_lost.q;ms_open.q;ms.q;ms_lamp_on.q;dir_set.q;drive_ok.q;demand.ed_brake;demand.traction;pant_moving.q;pant_not_up.q;snd_base.q;snd_gain.q;bat:position;pant_sel:position;pant_raise:position;pant_lower:position;main_sw:position;reverser:position;lever:position;pant_cmd:state;pant_up:elapsed;ms_init:elapsed;ms:state;demand:fault;lamp_ms:lit;lamp_pant:lit;v_hv:value;v_demand:value;snd_inverter:playing;snd_inverter:volume;snd_inverter:pitch +0.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +0.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +0.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +0.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +0.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +0.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +0.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +0.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +0.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +1.1;1;1;1;1;1;1;0;0;0;1;0;1;0;15;1;1;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;1;0;1;1;15;1;0.09;0;0;0;1;1;0;0;0;0.300000012;1 +1.2;1;1;1;1;1;1;0;0;0;1;0;1;0;15;1;1;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;1;0;1;1;15;1;0.19;0;0;0;1;1;0;0;0;0.300000012;1 +1.3;1;1;1;1;1;1;0;0;0;1;0;1;0;15;1;1;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;1;0;1;1;15;1;0.29;0;0;0;1;1;0;0;0;0.300000012;1 +1.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.39;0;0;0;1;1;0;0;0;0.300000012;1 +1.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.49;0;0;0;1;0;0;0;0;0.300000012;1 +1.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.59;0;0;0;1;0;0;0;0;0.300000012;1 +1.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.69;0;0;0;1;0;0;0;0;0.300000012;1 +1.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.79;0;0;0;1;0;0;0;0;0.300000012;1 +1.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.89;0;0;0;1;0;0;0;0;0.300000012;1 +2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.99;0;0;0;1;1;0;0;0;0.300000012;1 +2.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.09;0;0;0;1;1;0;0;0;0.300000012;1 +2.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.19;0;0;0;1;1;0;0;0;0.300000012;1 +2.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.29;0;0;0;1;1;0;0;0;0.300000012;1 +2.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.39;0;0;0;1;1;0;0;0;0.300000012;1 +2.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.49;0;0;0;1;1;0;0;0;0.300000012;1 +2.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.59;0;0;0;1;0;0;0;0;0.300000012;1 +2.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.69;0;0;0;1;0;0;0;0;0.300000012;1 +2.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.79;0;0;0;1;0;0;0;0;0.300000012;1 +2.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.89;0;0;0;1;0;0;0;0;0.300000012;1 +3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.99;0;0;0;1;0;0;0;0;0.300000012;1 +3.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.09;0;0;0;1;1;0;0;0;0.300000012;1 +3.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.19;0;0;0;1;1;0;0;0;0.300000012;1 +3.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.29;0;0;0;1;1;0;0;0;0.300000012;1 +3.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.39;0;0;0;1;1;0;0;0;0.300000012;1 +3.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.49;0;0;0;1;1;0;0;0;0.300000012;1 +3.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.59;0;0;0;1;0;0;0;0;0.300000012;1 +3.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.69;0;0;0;1;0;0;0;0;0.300000012;1 +3.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.79;0;0;0;1;0;0;0;0;0.300000012;1 +3.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.89;0;0;0;1;0;0;0;0;0.300000012;1 +4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.99;0;0;0;1;0;0;0;0;0.300000012;1 +4.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.09;0;0;0;1;1;0;0;0;0.300000012;1 +4.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.19;0;0;0;1;1;0;0;0;0.300000012;1 +4.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.29;0;0;0;1;1;0;0;0;0.300000012;1 +4.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.39;0;0;0;1;1;0;0;0;0.300000012;1 +4.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.49;0;0;0;1;1;0;0;0;0.300000012;1 +4.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.59;0;0;0;1;0;0;0;0;0.300000012;1 +4.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.69;0;0;0;1;0;0;0;0;0.300000012;1 +4.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.79;0;0;0;1;0;0;0;0;0.300000012;1 +4.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.89;0;0;0;1;0;0;0;0;0.300000012;1 +5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.99;0;0;0;1;0;0;0;0;0.300000012;1 +5.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.09;0;0;0;1;1;0;0;0;0.300000012;1 +5.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.19;0;0;0;1;1;0;0;0;0.300000012;1 +5.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.29;0;0;0;1;1;0;0;0;0.300000012;1 +5.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.39;0;0;0;1;1;0;0;0;0.300000012;1 +5.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.49;0;0;0;1;1;0;0;0;0.300000012;1 +5.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.59;0;0;0;1;0;0;0;0;0.300000012;1 +5.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.69;0;0;0;1;0;0;0;0;0.300000012;1 +5.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.79;0;0;0;1;0;0;0;0;0.300000012;1 +5.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.89;0;0;0;1;0;0;0;0;0.300000012;1 +6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.99;0;0;0;1;0;0;0;0;0.300000012;1 +6.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.08;0;0;1;0;3000;0;0;0.300000012;1 +6.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.18;0;0;1;0;3000;0;0;0.300000012;1 +6.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.28;0;0;1;0;3000;0;0;0.300000012;1 +6.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.38;0;0;1;0;3000;0;0;0.300000012;1 +6.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.48;0;0;1;0;3000;0;0;0.300000012;1 +6.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.58;0;0;1;0;3000;0;0;0.300000012;1 +6.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.68;0;0;1;0;3000;0;0;0.300000012;1 +6.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.78;0;0;1;0;3000;0;0;0.300000012;1 +6.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.88;0;0;1;0;3000;0;0;0.300000012;1 +7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.98;0;0;1;0;3000;0;0;0.300000012;1 +7.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.08;0;0;1;0;3000;0;0;0.300000012;1 +7.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.18;0;0;1;0;3000;0;0;0.300000012;1 +7.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.28;0;0;1;0;3000;0;0;0.300000012;1 +7.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.38;0;0;1;0;3000;0;0;0.300000012;1 +7.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.48;0;0;1;0;3000;0;0;0.300000012;1 +7.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.58;0;0;1;0;3000;0;0;0.300000012;1 +7.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.68;0;0;1;0;3000;0;0;0.300000012;1 +7.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.78;0;0;1;0;3000;0;0;0.300000012;1 +7.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.88;0;0;1;0;3000;0;0;0.300000012;1 +8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.98;0;0;1;0;3000;0;0;0.300000012;1 +8.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.08;0;0;1;0;3000;0;0;0.300000012;1 +8.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.18;0;0;1;0;3000;0;0;0.300000012;1 +8.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.28;0;0;1;0;3000;0;0;0.300000012;1 +8.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.38;0;0;1;0;3000;0;0;0.300000012;1 +8.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.48;0;0;1;0;3000;0;0;0.300000012;1 +8.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.58;0;0;1;0;3000;0;0;0.300000012;1 +8.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.68;0;0;1;0;3000;0;0;0.300000012;1 +8.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.78;0;0;1;0;3000;0;0;0.300000012;1 +8.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.88;0;0;1;0;3000;0;0;0.300000012;1 +9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.98;0;0;1;0;3000;0;0;0.300000012;1 +9.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.08;0;0;1;0;3000;0;0;0.300000012;1 +9.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.18;0;0;1;0;3000;0;0;0.300000012;1 +9.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.28;0;0;1;0;3000;0;0;0.300000012;1 +9.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.38;0;0;1;0;3000;0;0;0.300000012;1 +9.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.48;0;0;1;0;3000;0;0;0.300000012;1 +9.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.58;0;0;1;0;3000;0;0;0.300000012;1 +9.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.68;0;0;1;0;3000;0;0;0.300000012;1 +9.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.78;0;0;1;0;3000;0;0;0.300000012;1 +9.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.88;0;0;1;0;3000;0;0;0.300000012;1 +10;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.98;0;0;1;0;3000;0;0;0.300000012;1 +10.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.08;0;0;1;0;3000;0;0;0.300000012;1 +10.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.18;0;0;1;0;3000;0;0;0.300000012;1 +10.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.28;0;0;1;0;3000;0;0;0.300000012;1 +10.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.38;0;0;1;0;3000;0;0;0.300000012;1 +10.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.48;0;0;1;0;3000;0;0;0.300000012;1 +10.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.58;0;0;1;0;3000;0;0;0.300000012;1 +10.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.68;0;0;1;0;3000;0;0;0.300000012;1 +10.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.78;0;0;1;0;3000;0;0;0.300000012;1 +10.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.88;0;0;1;0;3000;0;0;0.300000012;1 +11;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.98;0;0;1;0;3000;0;0;0.300000012;1 +11.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.08;0;0;1;0;3000;0;0;0.300000012;1 +11.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.18;0;0;1;0;3000;0;0;0.300000012;1 +11.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.28;0;0;1;0;3000;0;0;0.300000012;1 +11.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.38;0;0;1;0;3000;0;0;0.300000012;1 +11.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.48;0;0;1;0;3000;0;0;0.300000012;1 +11.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.58;0;0;1;0;3000;0;0;0.300000012;1 +11.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.68;0;0;1;0;3000;0;0;0.300000012;1 +11.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.78;0;0;1;0;3000;0;0;0.300000012;1 +11.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.88;0;0;1;0;3000;0;0;0.300000012;1 +12;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.98;0;0;1;0;3000;0;0;0.300000012;1 +12.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.08;0;0;1;0;3000;0;0;0.300000012;1 +12.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.18;0;0;1;0;3000;0;0;0.300000012;1 +12.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.28;0;0;1;0;3000;0;0;0.300000012;1 +12.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.38;0;0;1;0;3000;0;0;0.300000012;1 +12.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.48;0;0;1;0;3000;0;0;0.300000012;1 +12.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.58;0;0;1;0;3000;0;0;0.300000012;1 +12.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.68;0;0;1;0;3000;0;0;0.300000012;1 +12.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.78;0;0;1;0;3000;0;0;0.300000012;1 +12.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.88;0;0;1;0;3000;0;0;0.300000012;1 +13;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.98;0;0;1;0;3000;0;0;0.300000012;1 +13.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.08;0;0;1;0;3000;0;0;0.300000012;1 +13.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.18;0;0;1;0;3000;0;0;0.300000012;1 +13.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.28;0;0;1;0;3000;0;0;0.300000012;1 +13.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.38;0;0;1;0;3000;0;0;0.300000012;1 +13.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.48;0;0;1;0;3000;0;0;0.300000012;1 +13.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.58;0;0;1;0;3000;0;0;0.300000012;1 +13.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.68;0;0;1;0;3000;0;0;0.300000012;1 +13.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.78;0;0;1;0;3000;0;0;0.300000012;1 +13.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.88;0;0;1;0;3000;0;0;0.300000012;1 +14;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.98;0;0;1;0;3000;0;0;0.300000012;1 +14.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.08;0;0;1;0;3000;0;0;0.300000012;1 +14.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.18;0;0;1;0;3000;0;0;0.300000012;1 +14.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.28;0;0;1;0;3000;0;0;0.300000012;1 +14.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.38;0;0;1;0;3000;0;0;0.300000012;1 +14.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.48;0;0;1;0;3000;0;0;0.300000012;1 +14.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.58;0;0;1;0;3000;0;0;0.300000012;1 +14.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.68;0;0;1;0;3000;0;0;0.300000012;1 +14.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.78;0;0;1;0;3000;0;0;0.300000012;1 +14.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.88;0;0;1;0;3000;0;0;0.300000012;1 +15;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.98;0;0;1;0;3000;0;0;0.300000012;1 +15.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.08;0;0;1;0;3000;0;0;0.300000012;1 +15.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.18;0;0;1;0;3000;0;0;0.300000012;1 +15.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.28;0;0;1;0;3000;0;0;0.300000012;1 +15.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.38;0;0;1;0;3000;0;0;0.300000012;1 +15.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.48;0;0;1;0;3000;0;0;0.300000012;1 +15.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.58;0;0;1;0;3000;0;0;0.300000012;1 +15.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.68;0;0;1;0;3000;0;0;0.300000012;1 +15.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.78;0;0;1;0;3000;0;0;0.300000012;1 +15.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.88;0;0;1;0;3000;0;0;0.300000012;1 +16;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.98;0;0;1;0;3000;0;0;0.300000012;1 +16.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +16.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +16.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +16.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +16.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +16.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +16.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +16.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +16.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +17;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +17.1;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +17.2;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +17.3;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +17.4;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +17.5;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +17.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +17.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +17.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +17.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +18;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +18.1;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +18.2;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +18.3;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +18.4;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +18.5;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +18.6;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +18.7;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +18.8;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +18.9;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +19;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +19.1;1;1;1;1;0;0;0;0;0;1;1;2;0.0666666667;16;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.05;0;0;0.3;0.35;1;1;0;0;1;2;16;1;5;10;1;0;0;0;3000;0.0500000007;1;0.349999994;1 +19.2;1;1;1;1;0;0;0;0;0;1;1;2;0.133333333;17;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.1;0;0;0.3;0.4;1;1;0;0;1;2;17;1;5;10;1;0;0;0;3000;0.100000001;1;0.400000006;1 +19.3;1;1;1;1;0;0;0;0;0;1;1;2;0.2;18;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.15;0;0;0.3;0.45;1;1;0;0;1;2;18;1;5;10;1;0;0;0;3000;0.150000006;1;0.449999988;1 +19.4;1;1;1;1;0;0;0;0;0;1;1;2;0.266666667;19;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.2;0;0;0.3;0.5;1;1;0;0;1;2;19;1;5;10;1;0;0;0;3000;0.200000003;1;0.5;1 +19.5;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.25;0;0;0.3;0.55;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.25;1;0.550000012;1 +19.6;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.3;0;0;0.3;0.6;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.300000012;1;0.600000024;1 +19.7;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +19.8;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +19.9;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +20;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +20.1;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +20.2;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +20.3;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +20.4;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +20.5;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +20.6;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +20.7;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +20.8;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +20.9;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +21;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0;0.333333333;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;0;3000;0.333333343;1;0.633333325;1 +21.1;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.05;0.233333333;0;0;0.3;0.533333333;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0.233333334;1;0.533333361;1 +21.2;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.1;0.133333333;0;0;0.3;0.433333333;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0.13333334;1;0.433333337;1 +21.3;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.15;0.0333333333;0;0;0.3;0.333333333;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0.0333333351;1;0.333333343;1 +21.4;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.2;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +21.5;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.25;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +21.6;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.3;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +21.7;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +21.8;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +21.9;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +22;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +22.1;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +22.2;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +22.3;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +22.4;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +22.5;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +22.6;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +22.7;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +22.8;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +22.9;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +23;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;1;1;0.333333333;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;0;3000;0;1;0.300000012;1 +23.1;1;1;1;1;0;0;0;0;-1;0;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;1;1;0;0;1;0;1;1;0;0.243333333;0;0;0;0.3;0.3;1;1;0;0;0;2;10;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +23.2;1;1;1;1;0;0;0;0;-1;0;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;1;1;0;0;1;0;1;1;0;0.143333333;0;0;0;0.3;0.3;1;1;0;0;0;2;10;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +23.3;1;1;1;1;0;0;0;0;-1;0;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;1;1;0;0;1;0;1;1;0;0.0433333333;0;0;0;0.3;0.3;1;1;0;0;0;2;10;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +23.4;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +23.5;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +23.6;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +23.7;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +23.8;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +23.9;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +24;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;0;1;0;3000;0;0;0.300000012;1 +24.1;1;1;1;1;0;0;1;1;0;1;1;2;-0.333333333;10;0;0;1;0;0;3000;0;0;0;0;0;0;1;1;0;1;1;0;0;0;0;1;0.3;0.3;1;1;0;1;1;2;10;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +24.2;1;1;1;1;0;0;1;1;0;1;1;2;-0.333333333;10;0;0;1;0;0;3000;0;0;0;0;0;0;1;1;0;1;1;0;0;0;0;1;0.3;0.3;1;1;0;1;1;2;10;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +24.3;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +24.4;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +24.5;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +24.6;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +24.7;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +24.8;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +24.9;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +25;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +25.1;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;0;1;0;0;0;0;0.300000012;1 +25.2;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;0;1;0;0;0;0;0.300000012;1 diff --git a/tools/vehicle_graph/examples/vectron-logic.vhg b/tools/vehicle_graph/examples/vectron-logic.vhg index d06cc7c6..0c25cb4d 100644 --- a/tools/vehicle_graph/examples/vectron-logic.vhg +++ b/tools/vehicle_graph/examples/vectron-logic.vhg @@ -1,7 +1,7 @@ // cab logic of an electric locomotive with induction motors, modelled after the Vectron .fiz. // stage 1 of the vehicle graph: only logic and cab blocks are available, so the pantograph, the main circuit -// and the inverters are represented by timers and constants. the combined power/brake lever is split into -// a traction demand and an electrodynamic brake demand, which will drive the inverter blocks in the next stages +// and the inverters are represented by timers and constants. the combined power/brake lever goes through a lua +// block, which gives a traction demand and an electrodynamic brake demand for the inverter blocks of the next stages { "format": "maszyna-vehicle-graph", "version": 2, @@ -15,7 +15,6 @@ { "id": "main_sw", "type": "cab.switch", "title": "Main switch", "params": { "behaviour": "multi", "positions": 3, "min": -1, "max": 1, "default": 0, "spring": true, "labels": "off;0;on", "step": 40, "offset": -40, "submodel": "main_sw:" } }, { "id": "reverser", "type": "cab.switch", "title": "Direction", "params": { "behaviour": "multi", "positions": 3, "min": -1, "max": 1, "default": 0, "labels": "back;0;forward", "step": 45, "offset": -45, "submodel": "dirkey:" } }, { "id": "lever", "type": "cab.switch", "title": "Power/brake lever", "params": { "behaviour": "multi", "positions": 31, "min": -1, "max": 1, "default": 0, "step": 2, "offset": -30, "submodel": "jointctrl:" } }, - { "id": "pant_cmd", "type": "logic.sr", "title": "Pantographs commanded up" }, { "id": "pant_cmd_on", "type": "logic.and", "title": "Raising with battery" }, { "id": "pant_cmd_off", "type": "logic.nand", "title": "Lowering or battery off" }, @@ -24,7 +23,6 @@ { "id": "hv_nominal", "type": "logic.const", "title": "Catenary voltage", "params": { "value": 3000 } }, { "id": "hv", "type": "logic.math", "title": "Voltage behind the pantograph", "params": { "op": "mul" } }, { "id": "hv_ok", "type": "logic.hyst", "title": "Voltage above minimum", "params": { "lo": 1800, "hi": 1900 } }, - { "id": "ms_on_req", "type": "logic.compare", "title": "Main switch to on", "params": { "op": "gt", "value": 0 } }, { "id": "ms_off_req", "type": "logic.compare", "title": "Main switch to off", "params": { "op": "lt", "value": 0 } }, { "id": "ms_init", "type": "logic.ton", "title": "Main circuit initialisation", "params": { "time": 10.0 } }, @@ -33,24 +31,15 @@ { "id": "ms_open", "type": "logic.or", "title": "Opening" }, { "id": "ms", "type": "logic.sr", "title": "Main circuit breaker" }, { "id": "ms_lamp_on", "type": "logic.not", "title": "Main circuit open" }, - - { "id": "zero", "type": "logic.const", "title": "Zero", "params": { "value": 0 } }, - { "id": "minus_one", "type": "logic.const", "title": "Minus one", "params": { "value": -1 } }, - { "id": "lever_pos", "type": "logic.math", "title": "Traction part", "params": { "op": "max" } }, - { "id": "lever_neg", "type": "logic.math", "title": "Lever inverted", "params": { "op": "mul" } }, - { "id": "lever_brake", "type": "logic.math", "title": "Brake part", "params": { "op": "max" } }, { "id": "dir_set", "type": "logic.compare", "title": "Direction selected", "params": { "op": "ne", "value": 0 } }, { "id": "drive_ok", "type": "logic.and", "title": "Ready to drive" }, - { "id": "traction", "type": "logic.select", "title": "Traction demand" }, - { "id": "ed_brake", "type": "logic.select", "title": "ED brake demand" }, - + { "id": "demand", "type": "lua.block", "title": "Power/brake demand", "params": { "lua.ramp_up": 0.5, "lua.ramp_down": 1.0 }, "lua": "-- combined power/brake lever of the Vectron: forward of zero sets the traction demand, back of zero the\n-- electrodynamic brake demand. both follow the lever with limited rates, and drop to zero when the locomotive\n-- isn't ready to drive (no direction selected or main circuit open)\nlocal function approach(value, target, up, down, dt)\n if target > value then\n return math.min(target, value + up * dt)\n end\n return math.max(target, value - down * dt)\nend\n\nblock \"power_brake\" {\n inputs = { lever = \"float\", ready = \"bool\" },\n outputs = { traction = \"float\", ed_brake = \"float\" },\n params = { ramp_up = 0.5, ramp_down = 1.0 }, -- per second\n state = { t = 0.0, b = 0.0 },\n\n update = function(self, i, o, dt)\n local want_t, want_b = 0.0, 0.0\n if i.ready then\n want_t = math.max(i.lever, 0.0)\n want_b = math.max(-i.lever, 0.0)\n end\n self.t = approach(self.t, want_t, self.ramp_up, self.ramp_down, dt)\n self.b = approach(self.b, want_b, self.ramp_up, self.ramp_down, dt)\n o.traction = self.t\n o.ed_brake = self.b\n end,\n}\n" }, { "id": "lamp_ms", "type": "fb.lamp", "title": "Main circuit lamp", "params": { "color": "red", "submodel": "i-mainbreaker:" } }, { "id": "lamp_pant", "type": "fb.lamp", "title": "Pantograph lamp", "params": { "color": "yellow", "blink": true, "blink_period": 1.0, "submodel": "i-pantraising:" } }, { "id": "pant_moving", "type": "logic.and", "title": "Pantograph moving" }, { "id": "pant_not_up", "type": "logic.not", "title": "Pantograph not up yet" }, { "id": "v_hv", "type": "fb.gauge", "title": "Catenary voltage", "params": { "min": 0, "max": 4000, "unit": "V", "angle_min": 0, "angle_max": 270, "submodel": "hvoltage:" } }, { "id": "v_demand", "type": "fb.gauge", "title": "Traction demand", "params": { "min": 0, "max": 1, "angle_min": 0, "angle_max": 270, "submodel": "tractionforce:" } }, - { "id": "snd_base", "type": "logic.const", "title": "Idle volume", "params": { "value": 0.3 } }, { "id": "snd_gain", "type": "logic.math", "title": "Volume rising with demand", "params": { "op": "add" } }, { "id": "snd_inverter", "type": "fb.sound", "title": "Inverters", "params": { "file": "vectron-inverter.wav", "placement": "engine", "loop": true } } @@ -72,7 +61,6 @@ { "from": "pant_cmd.q", "to": "pant_moving.a" }, { "from": "pant_not_up.q", "to": "pant_moving.b" }, { "from": "pant_moving.q", "to": "lamp_pant.on" }, - { "from": "main_sw.pos", "to": "ms_on_req.a" }, { "from": "main_sw.pos", "to": "ms_off_req.a" }, { "from": "hv_ok.q", "to": "ms_init.a" }, @@ -86,27 +74,15 @@ { "from": "ms_open.q", "to": "ms.r" }, { "from": "ms.q", "to": "ms_lamp_on.a" }, { "from": "ms_lamp_on.q", "to": "lamp_ms.on" }, - - { "from": "lever.pos", "to": "lever_pos.a" }, - { "from": "zero.q", "to": "lever_pos.b" }, - { "from": "lever.pos", "to": "lever_neg.a" }, - { "from": "minus_one.q", "to": "lever_neg.b" }, - { "from": "lever_neg.q", "to": "lever_brake.a" }, - { "from": "zero.q", "to": "lever_brake.b" }, + { "from": "lever.pos", "to": "demand.lever" }, { "from": "reverser.pos", "to": "dir_set.a" }, { "from": "dir_set.q", "to": "drive_ok.a" }, + { "from": "drive_ok.q", "to": "demand.ready" }, { "from": "ms.q", "to": "drive_ok.b" }, - { "from": "drive_ok.q", "to": "traction.sel" }, - { "from": "zero.q", "to": "traction.a" }, - { "from": "lever_pos.q", "to": "traction.b" }, - { "from": "drive_ok.q", "to": "ed_brake.sel" }, - { "from": "zero.q", "to": "ed_brake.a" }, - { "from": "lever_brake.q", "to": "ed_brake.b" }, - { "from": "traction.q", "to": "v_demand.value" }, - + { "from": "demand.traction", "to": "v_demand.value" }, { "from": "ms.q", "to": "snd_inverter.play" }, { "from": "snd_base.q", "to": "snd_gain.a" }, - { "from": "traction.q", "to": "snd_gain.b" }, + { "from": "demand.traction", "to": "snd_gain.b" }, { "from": "snd_gain.q", "to": "snd_inverter.volume" } ] } diff --git a/tools/vehicle_graph/examples/vectron.expected.csv b/tools/vehicle_graph/examples/vectron.expected.csv new file mode 100644 index 00000000..d6fb54a5 --- /dev/null +++ b/tools/vehicle_graph/examples/vectron.expected.csv @@ -0,0 +1,92 @@ +time;bat.pos;bat.index;pant_sel.pos;pant_sel.index;pant_raise.pos;pant_raise.index;pant_lower.pos;pant_lower.index;main_sw.pos;main_sw.index;reverser.pos;reverser.index;lever.pos;lever.index;brake.pos;brake.index;local.pos;local.index;sb_release.pos;sb_release.index;ca_reset.pos;ca_reset.index;shp_reset.pos;shp_reset.index;sand_bt.pos;sand_bt.index;horn_lo.pos;horn_lo.index;horn_hi.pos;horn_hi.index;lights_sw.pos;lights_sw.index;heat_sw.pos;heat_sw.index;pant_cmd_on.q;no_lower.q;pant_cmd_off.q;pant_cmd.q;sel_front.q;sel_both.q;sel_front_any.q;sel_rear.q;pant_f_up.q;pant_r_up.q;pf.up;pf.height;pf.u;pr.up;pr.height;pr.u;ms_on_req.q;ms_off_req.q;ms_close.q;ms.closed;ms.i;ms.tripped;conv.run;batt.ok;batt.u;heat.i;dir_set.q;drive_ok.q;demand.ed_brake;demand.traction;set_force.q;motors.i;motors.f;motors.ed_active;motors.ed;axles.force;axles.slip;comp_on.q;comp.run;gov.need;res.p;k8p.p_ctl;pipe.p;dist.bc;dist.aux;dist.crp;rig.force;spring.force;spring.applied;vs.v;vs.v_signed;vs.accel;vs.wheel_rpm;vs.slipping;vs.cab;vs.time;ca.lamp;ca.buzzer;ca.brake;ca.shp_lamp;sand.sanding;pc1.q;pc_front.q;ms_open.q;ms_lamp.q;pant_not_up.q;pant_moving.q;sb_lamp.q;hv_volts.q;snd_base.q;snd_gain.q;bat:position;pant_sel:position;pant_raise:position;pant_lower:position;main_sw:position;reverser:position;lever:position;brake:position;local:position;sb_release:position;ca_reset:position;shp_reset:position;sand_bt:position;horn_lo:position;horn_hi:position;lights_sw:position;heat_sw:position;pant_cmd:state;pf:height;pr:height;ms:closed;batt:charge;demand:fault;motors:set_value;motors:force_kn;gov:need;lamp_ms:lit;lamp_pant:lit;lamp_sb:lit;lamp_ca:lit;lamp_shp:lit;g_hv:value;g_force:value;g_v:value;g_pipes:value;g_pipes:value2;g_bc:value;snd_inverter:playing;snd_inverter:volume;snd_inverter:pitch;snd_comp:playing;snd_comp:volume;snd_comp:pitch;snd_buzzer:playing;snd_buzzer:volume;snd_buzzer:pitch;snd_horn_lo:playing;snd_horn_lo:volume;snd_horn_lo:pitch;snd_horn_hi:playing;snd_horn_hi:volume;snd_horn_hi:pitch;body:v;body:ft;body:fb;body:pg;body:zg;body:bc +1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.32085695;0;4.9952586;0;8.05070722;4.99303058;25076.736;11.6096;1;0;0;0;0;0;1;1;0;1;0;1;0;21;0;1;1;1;0;1;0;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;0;0;0;0;200;0;0;0;1;1;0;1;0;1;0;0;0;4.99525881;8.32085705;0;0;0.300000012;1;0;1;1;1;1;1;0;1;1;0;1;1;0;0;25.076736;4.99525639;8.32085695;0 +2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;0;0.198;0;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.31251534;0;4.99652491;0;8.09555912;4.99320364;0;0;1;0;0;0;0;0;1;2;0;1;0;1;0;21;0;1;1;1;1;1;0;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;0.198;0;0;200;0;0;0;1;1;1;1;0;1;0;0;0;4.99652481;8.31251526;0;0;0.300000012;1;0;1;1;1;1;1;0;1;1;0;1;1;0;0;0;4.9965218;8.31251534;0 +3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;0;0.398;0;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.30552741;0;4.99722555;0;8.13164508;4.99350314;0;0;0;0;0;0;0;0;1;3;0;0;0;0;0;21;0;1;1;1;1;0;0;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;0.398;0;0;200;0;0;0;1;1;0;0;0;0;0;0;0;4.99722576;8.30552769;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99722178;8.30552741;0 +4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;0;0.598;0;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.2996546;0;4.99767206;0;8.16050239;4.99386605;0;0;0;0;0;0;0;0;1;4;0;0;0;0;0;21;0;1;1;1;1;0;0;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;0.598;0;0;200;0;0;0;1;1;0;0;0;0;0;0;0;4.99767208;8.29965496;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99766804;8.2996546;0 +5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;0;0.798;0;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.29467963;0;4.99798223;0;8.18353387;4.99425124;0;0;0;0;0;0;0;0;1;5;0;0;0;0;0;21;0;1;1;1;1;0;0;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;0.798;0;0;200;0;0;0;1;1;0;0;0;0;0;0;0;4.99798203;8.29467964;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99797819;8.29467963;0 +6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;0;0.998;0;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.29042819;0;4.99821057;0;8.20186917;4.99463607;0;0;0;0;0;0;0;0;1;6;0;0;0;0;0;21;0;1;1;1;1;0;0;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;0.998;0;0;200;0;0;0;1;1;0;0;0;0;0;0;0;4.99821043;8.29042816;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99820665;8.29042819;0 +7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.28676003;0;4.99838586;0;8.2164183;4.9950086;0;0;0;0;0;0;0;0;1;7;0;0;0;0;0;21;0;1;1;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;0;200;0;0;0;1;1;0;0;0;0;3000;0;0;4.99838591;8.28676033;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99838213;8.28676003;0 +8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.28356222;0;4.99852476;0;8.22791455;4.9953627;0;0;0;0;0;0;0;0;1;8;0;0;0;0;0;21;0;1;1;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;0;200;0;0;0;1;1;0;0;0;0;3000;0;0;4.99852467;8.28356266;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99852124;8.28356222;0 +9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.28074376;0;4.99863759;0;8.23694894;4.99569547;0;0;0;0;0;0;0;0;1;9;0;0;0;0;0;21;0;1;1;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;0;200;0;0;0;1;1;0;0;0;0;3000;0;0;4.99863768;8.2807436;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.9986343;8.28074376;0 +10;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.2782313;0;4.99873109;0;8.2439979;4.99600582;0;0;0;0;0;0;0;0;1;10;0;0;0;0;0;21;0;1;1;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;0;200;0;0;0;1;1;0;0;0;0;3000;0;0;4.99873114;8.27823162;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99872803;8.2782313;0 +11;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.2759657;0;4.99880985;0;8.24944557;4.99629374;0;0;0;0;0;0;0;0;1;11;0;0;0;0;0;21;0;1;1;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;0;200;0;0;0;1;1;0;0;0;0;3000;0;0;4.99880981;8.27596569;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99880702;8.2759657;0 +12;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.27389928;0;4.99887712;0;8.25360168;4.99655982;0;0;0;0;0;0;0;0;1;12;0;0;0;0;0;21;0;1;1;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;0;200;0;0;0;1;1;0;0;0;0;3000;0;0;4.99887705;8.27389908;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.9988745;8.27389928;0 +13;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.27199358;0;4.99893523;0;8.25671601;4.99680502;0;0;0;0;0;0;0;0;1;13;0;0;0;0;0;21;0;1;1;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;0;200;0;0;0;1;1;0;0;0;0;3000;0;0;4.99893522;8.27199364;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99893282;8.27199358;0 +14;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.27021758;0;4.99898594;0;8.25899001;4.99703048;0;0;0;0;0;0;0;0;1;14;0;0;0;0;0;21;0;1;1;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;0;200;0;0;0;1;1;0;0;0;0;3000;0;0;4.99898577;8.2702179;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99898372;8.27021758;0 +15;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.26854625;0;4.99903057;0;8.26058615;4.99723746;0;0;0;0;0;0;0;0;1;15;0;0;0;0;0;21;0;1;1;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;0;200;0;0;0;1;1;0;0;0;0;3000;0;0;4.99903059;8.2685461;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99902853;8.26854625;0 +16;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.2669594;0;4.99907016;0;8.26163549;4.99742722;0;0;0;0;0;0;0;0;1;16;0;0;0;0;0;21;0;1;1;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;0;200;0;0;0;1;1;0;0;0;0;3000;0;0;4.99907017;8.26695919;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99906828;8.2669594;0 +17;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.26544076;0;4.99910549;0;8.26224374;4.99760104;0;0;0;0;0;0;0;0;1;17;0;0;0;0;0;21;0;1;1;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;0;200;0;0;0;1;1;0;0;0;0;3000;0;0;4.99910545;8.26544094;0;0;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99910376;8.26544076;0 +18;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.26397718;0;4.99913722;0;8.26249616;4.99776012;0;0;0;0;0;0;0;0;1;18;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0;0;1;0;0;0;0;0;3000;0;0;4.9991374;8.26397705;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99913564;8.26397718;0 +19;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.26255804;0;4.99916587;0;8.26246153;4.99790565;0;0;0;0;0;0;0;0;1;19;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0;0;1;0;0;0;0;0;3000;0;0;4.99916601;8.26255798;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99916441;8.26255804;0 +20;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.2611597;0;4.99919185;0;8.26229638;4.99803873;0;0;0;0;0;0;0;0;1;20;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0;0;1;0;0;0;0;0;3000;0;0;4.99919176;8.2611599;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99919051;8.2611597;0 +21;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.25976166;0;4.99921552;0;8.26213113;4.99816039;0;0;0;0;0;0;0;0;1;21;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0;0;1;0;0;0;0;0;3000;0;0;4.9992156;8.25976181;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99921429;8.25976166;0 +22;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.25836387;0;4.99923717;0;8.26196589;4.9982716;0;0;0;0;0;0;0;0;1;22;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0;0;1;0;0;0;0;0;3000;0;0;4.99923706;8.25836372;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99923604;8.25836387;0 +23;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.25696632;0;4.99925704;0;8.26180066;4.99837325;0;0;0;0;0;0;0;0;1;23;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0;0;1;0;0;0;0;0;3000;0;0;4.99925709;8.25696659;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.999256;8.25696632;0 +24;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.25556897;0;4.99927534;0;8.26163542;4.99846617;0;0;0;0;0;0;0;0;1;24;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0;0;1;0;0;0;0;0;3000;0;0;4.99927521;8.2555685;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99927438;8.25556897;0 +25;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.25417181;0;4.99929225;0;8.26147019;4.99855112;0;0;0;0;0;0;0;0;1;25;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0;0;1;0;0;0;0;0;3000;0;0;4.99929237;8.25417137;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99929136;8.25417181;0 +26;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.25277483;0;4.99930791;0;8.26130496;4.99862881;0;0;0;0;0;0;0;0;1;26;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0;0;1;0;0;0;0;0;3000;0;0;4.99930811;8.25277519;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99930709;8.25277483;0 +27;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;0;0;0;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;8.25137801;0;4.99932245;0;8.26113974;4.99869986;0;0;0;0;0;0;0;0;1;27;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0;0;1;0;0;0;0;0;3000;0;0;4.99932241;8.25137806;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.9993217;8.25137801;0 +28;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;7;0;1;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;8.31798956;0;4.99933889;0;8.26427475;4.9987649;0;0;0;0;0;0;0;0;1;28;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0;0;1;0;0;0;0;0;3000;0;0;4.9993391;8.31798935;0;1;0.300000012;1;1;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99933819;8.31798956;0 +29;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;7;0;1;1;24;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;8.39919365;0;4.99935539;0;8.28018612;4.99882466;0;0;0;0;0;0;0;0;1;29;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;1;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0;0;1;0;0;0;0;0;3000;0;0;4.99935532;8.39919376;0;1;0.300000012;1;1;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99935473;8.39919365;0 +30;1;1;1;1;0;0;0;0;0;1;1;2;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;27;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;1;1;1;8.47810656;0;4.99937082;0;8.30712527;4.99887974;0;0;0;0;0;0;0;0;1;30;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0;0;1;0;0;0;0;0;3000;0;0;4.99937105;8.4781065;0;1;0.300000012;1;1;1;1;0;1;1;0;1;1;0;1;1;0;0;0;4.99937021;8.47810656;0 +31;1;1;1;1;0;0;0;0;0;1;1;2;0.533333333;23;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;85.2716575;0;1;1;24;0;1;1;0;0.5;0.5;391.53049;2.44122489;0;0;168137.804;0;1;1;1;8.55507809;0;4.99938531;0;8.34291852;4.99893058;0;0;0;2.83461923;0.78739423;1.61207676;12.0304977;0;1;31;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.8;1;1;0;0;1;2;23;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0.495970189;168.137804;1;0;0;0;0;0;3000;168137.797;2.83461928;4.99938536;8.55507851;0;1;0.800000012;1;1;1;1;0;1;1;0;1;1;0;1;1;2.89325377;168.137804;0;4.99938475;8.55507809;0 +32;1;1;1;1;0;0;0;0;0;1;1;2;0.533333333;23;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;202.698786;0;1;1;24;0;1;1;0;0.533333333;0.533333333;422.200152;6.67675311;0;0;181308.501;0;1;1;1;8.63039405;0;4.99939895;0;8.38579328;4.99897754;0;0;0;9.14112054;2.53920015;1.75737387;38.7961205;0;1;32;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.833333333;1;1;0;0;1;2;23;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0.538325933;181.308501;1;0;0;0;0;0;3000;181308.5;9.14112091;4.99939919;8.63039398;0;1;0.833333313;1;1;1;1;0;1;1;0;1;1;0;1;1;9.20438573;181.308501;0;4.99939842;8.63039405;0 +33;1;1;1;1;0;0;0;0;0;1;1;2;0.533333333;23;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;314.069461;0;1;1;24;0;1;1;0;0.533333333;0.533333333;422.199803;10.8822852;0;0;181308.352;0;1;1;1;8.70434564;0;4.99941183;0;8.43391183;4.99902094;0;0;0;15.4659748;4.29610411;1.75635689;65.6396358;0;1;33;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.833333333;1;1;0;0;1;2;23;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0.541887165;181.308352;1;0;0;0;0;0;3000;181308.344;15.4659748;4.99941206;8.7043457;0;1;0.833333313;1;1;1;1;0;1;1;0;1;1;0;1;1;15.5292032;181.308352;0;4.99941133;8.70434564;0 +34;1;1;1;1;0;0;0;0;0;1;1;2;0.533333333;23;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;425.359733;0;1;1;24;0;1;1;0;0.533333333;0.533333333;422.199468;15.084775;0;0;181308.207;0;1;1;1;8.77734927;0;4.99942402;0;8.48462681;4.99906109;0;0;0;21.786216;6.05172667;1.75481653;92.4635725;0;1;34;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.833333333;1;1;0;0;1;2;23;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0.545493202;181.308207;1;0;0;0;0;0;3000;181308.203;21.7862167;4.99942398;8.77734947;0;1;0.833333313;1;1;1;1;0;1;1;0;1;1;0;1;1;21.8493887;181.308207;0;4.99942355;8.77734927;0 +35;1;1;1;1;0;0;0;0;0;1;1;2;0.533333333;23;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;536.536487;0;1;1;24;0;1;1;0;0.533333333;0.533333333;422.199145;19.2829718;0;0;181308.069;0;1;1;1;8.8495025;0;4.99943557;0;8.53735971;4.99909826;0;0;0;28.0999631;7.80554531;1.75275464;119.259947;0;1;35;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.833333333;1;1;0;0;1;2;23;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0.549143797;181.308069;1;0;0;0;0;0;3000;181308.062;28.0999622;4.99943542;8.84950256;0;1;0.833333313;1;1;1;1;0;1;1;0;1;1;0;1;1;28.1630614;181.308069;0;4.99943514;8.8495025;0 +36;1;1;1;1;0;0;0;0;0;1;1;2;0.533333333;23;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;647.566746;0;1;1;24;0;1;1;0;0.533333333;0.533333333;422.198836;23.4756299;0;0;181307.936;0;1;1;1;8.92085474;0;4.99944654;0;8.59182898;4.99913269;0;0;0;34.4053428;9.55703968;1.75017365;146.02081;0;1;36;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.833333333;1;1;0;0;1;2;23;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0.552838669;181.307936;1;0;0;0;0;0;3000;181307.938;34.4053421;4.99944639;8.92085457;0;1;0.833333313;1;1;1;1;0;1;1;0;1;1;0;1;1;34.4683481;181.307936;0;4.99944613;8.92085474;0 +37;1;1;1;1;0;0;0;0;0;1;1;2;0.533333333;23;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;758.417706;0;1;1;24;0;1;1;0;0.533333333;0.533333333;422.198541;27.6615102;0;0;181307.81;0;1;1;1;8.9914477;0;4.99945697;0;8.64780097;4.99916463;0;0;0;40.7004919;11.3056922;1.74707665;172.738253;0;1;37;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.833333333;1;1;0;0;1;2;23;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0.556577506;181.30781;1;0;0;0;0;0;3000;181307.812;40.7004929;4.99945688;8.99144745;0;1;0.833333313;1;1;1;1;0;1;1;0;1;1;0;1;1;40.7633855;181.30781;0;4.99945658;8.9914477;0 +38;1;1;1;1;0;0;0;0;0;1;1;2;0.533333333;23;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;869.056777;0;1;1;24;0;1;1;0;0.533333333;0.533333333;422.198261;31.8393817;0;0;181307.689;0;1;1;1;9.06131725;0;4.9994669;0;8.70507795;4.99919427;0;0;0;46.9835591;13.0509886;1.74346729;199.404418;0;1;38;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.833333333;1;1;0;0;1;2;23;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0.560359959;181.307689;1;0;0;0;0;0;3000;181307.688;46.9835587;4.9994669;9.06131744;0;1;0.833333313;1;1;1;1;0;1;1;0;1;1;0;1;1;47.0463225;181.307689;0;4.99946654;9.06131725;0 +39;1;1;1;1;0;0;0;0;0;1;1;2;0.533333333;23;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;979.451618;0;1;1;24;0;1;1;0;0.533333333;0.533333333;422.197995;36.0080227;0;0;181307.575;0;1;1;1;9.13049474;0;4.99947637;0;8.76349002;4.9992218;0;0;0;53.2527076;14.7924188;1.73934985;226.01151;0;1;39;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.833333333;1;1;0;0;1;2;23;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0.564185646;181.307575;1;0;0;0;0;0;3000;181307.578;53.2527084;4.99947643;9.13049507;0;1;0.833333313;1;1;1;1;0;1;1;0;1;1;0;1;1;53.3153226;181.307575;0;4.99947602;9.13049474;0 +40;1;1;1;1;0;0;0;0;0;1;1;2;0.533333333;23;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;1087.34182;0;1;1;24;0;1;1;0;0.533333333;0.533333333;421.33669;40.163907;0;0;180937.699;0;1;1;1;9.19900794;0;4.99948541;0;8.82288928;4.99924742;0;0;0;59.5044672;16.5290187;1.73110125;252.544803;0;1;40;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.833333333;1;1;0;0;1;2;23;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;0.575138961;180.937699;1;0;0;0;0;0;3000;180937.703;59.504467;4.99948549;9.19900799;0;1;0.833333313;1;1;1;1;0;1;1;0;1;1;0;1;1;59.5667853;180.937699;0;4.99948508;9.19900794;0 +41;1;1;1;1;0;0;0;0;0;1;1;2;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;27;0;1;1;24;0;1;1;0;0;0;2.63602436e-11;40.7721583;0;0;1.13200724e-08;0;1;1;1;9.26688165;0;4.99949405;0;8.88314567;4.99927126;0;0;0;61.3162596;17.0322943;-0.044279417;260.234288;0;1;41;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;3.87830875e-14;1.13200724e-11;1;0;0;0;0;0;3000;1.1320072e-08;61.3162613;4.99949408;9.26688194;0;1;0.300000012;1;1;1;1;0;1;1;0;1;1;0;1;1;61.3146656;1.13200724e-11;0;4.99949373;9.26688165;0 +42;1;1;1;1;0;0;0;0;0;1;1;2;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;27;0;1;1;24;0;1;1;0;0;0;0;40.6663157;0;0;0;0;1;1;1;9.33413828;0;4.99950231;0;8.94414377;4.99929348;0;0;0;61.1570864;16.9880795;-0.0441515541;259.558736;0;1;42;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;1.54517169e-42;0;1;0;0;0;0;0;3000;0;61.1570854;4.99950218;9.33413792;0;1;0.300000012;1;1;1;1;0;1;1;0;1;1;0;1;1;61.155497;0;0;4.99950202;9.33413828;0 +43;1;1;1;1;0;0;0;0;0;1;1;2;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;27;0;1;1;24;0;1;1;0;0;0;0;40.5607783;0;0;0;0;1;1;1;9.40079816;0;4.99951023;0;9.00578044;4.99931422;0;0;0;60.9983722;16.9439923;-0.0440243909;258.885132;0;1;43;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;6.15617713e-71;0;1;0;0;0;0;0;3000;0;60.9983711;4.99951029;9.40079784;0;1;0.300000012;1;1;1;1;0;1;1;0;1;1;0;1;1;60.9967873;0;0;4.99950995;9.40079816;0 +44;1;1;1;1;0;0;0;0;0;1;1;2;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;27;0;1;1;24;0;1;1;0;0;0;0;40.4555445;0;0;0;0;1;1;1;9.46687991;0;4.99951782;0;9.06796291;4.99933359;0;0;0;60.8401145;16.9000318;-0.0438979225;258.213466;0;1;44;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;2.45270587e-99;0;1;0;0;0;0;0;3000;0;60.8401146;4.99951792;9.46687984;0;1;0.300000012;1;1;1;1;0;1;1;0;1;1;0;1;1;60.8385342;0;0;4.99951755;9.46687991;0 +45;1;1;1;1;0;0;0;0;0;1;1;2;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;27;0;1;1;24;0;1;1;0;0;0;0;40.3506126;0;0;0;0;1;1;1;9.53240063;0;4.99952511;0;9.13060729;4.9993517;0;0;0;60.6823108;16.8561975;-0.0437721441;257.543726;0;1;45;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;9.77191846e-128;0;1;0;0;0;0;0;3000;0;60.682312;4.99952507;9.53240108;0;1;0.300000012;1;1;1;1;0;1;1;0;1;1;0;1;1;60.6807351;0;0;4.99952485;9.53240063;0 +46;1;1;1;1;0;0;0;0;0;1;1;2;-0.666666667;5;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;-394.845774;0;1;1;24;0;1;1;0.5;0;-0.5;-173.482827;39.1408955;1;0.993333333;-74500;0;1;1;1;9.59737614;0;4.99953211;0;9.19363742;4.99936866;0;0;0;59.2332091;16.4536692;-0.765691847;251.393547;0;1;46;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;5;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-0.496666667;-74.5;1;0;0;0;0;0;3000;-74500;59.2332077;4.99953222;9.59737587;0;1;0.300000012;1;1;1;1;0;1;1;0;1;1;0;1;1;59.2053803;-74.5;0;4.99953186;9.59737614;0 +47;1;1;1;1;0;0;0;0;0;1;1;2;-0.666666667;5;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;-509.634219;0;1;1;24;0;1;1;0.666666667;0;-0.666666667;-232.862855;36.7163383;1;1;-100000;0;1;1;1;9.66182109;0;4.99953884;0;9.25698387;4.99938456;0;0;0;55.7126164;15.4757268;-1.02039612;236.451688;0;1;47;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;5;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-0.666666667;-100;1;0;0;0;0;0;3000;-100000;55.712616;4.9995389;9.66182137;0;1;0.300000012;1;1;1;1;0;1;1;0;1;1;0;1;1;55.6758831;-100;0;4.9995386;9.66182109;0 +48;1;1;1;1;0;0;0;0;0;1;1;2;-0.666666667;5;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;-474.002596;0;1;1;24;0;1;1;0.666666667;0;-0.666666667;-232.862855;34.2768746;1;1;-100000;0;1;1;1;9.72574912;0;4.99954531;0;9.32058317;4.99939948;0;0;0;52.0439622;14.4566562;-1.01780154;220.881436;0;1;48;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;5;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-0.666666667;-100;1;0;0;0;0;0;3000;-100000;52.0439606;4.9995451;9.72574902;0;1;0.300000012;1;1;1;1;0;1;1;0;1;1;0;1;1;52.0073223;-100;0;4.99954509;9.72574912;0 +49;1;1;1;1;0;0;0;0;0;1;1;2;-0.666666667;5;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;-438.458653;0;1;1;24;0;1;1;0.666666667;0;-0.666666667;-232.862855;31.8434095;1;1;-100000;0;1;1;1;9.78917295;0;4.99955155;0;9.38437713;4.99941351;0;0;0;48.3843167;13.440088;-1.01538925;205.349418;0;1;49;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;5;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-0.666666667;-100;1;0;0;0;0;0;3000;-100000;48.3843155;4.99955177;9.78917313;0;1;0.300000012;1;1;1;1;0;1;1;0;1;1;0;1;1;48.3477635;-100;0;4.99955134;9.78917295;0 +50;1;1;1;1;0;0;0;0;0;1;1;2;-0.666666667;5;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;-402.996045;0;1;1;24;0;1;1;0.666666667;0;-0.666666667;-232.862855;29.4155088;1;1;-100000;0;1;1;1;9.8521045;0;4.99955757;0;9.4483123;4.9994267;0;0;0;44.7330265;12.4258407;-1.01315754;189.852861;0;1;50;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;5;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-0.666666667;-100;1;0;0;0;0;0;3000;-100000;44.7330284;4.9995575;9.85210419;0;1;0.300000012;1;1;1;1;0;1;1;0;1;1;0;1;1;44.6965536;-100;0;4.99955736;9.8521045;0 +51;1;1;1;1;0;0;0;0;0;1;1;2;-0.666666667;5;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;-372.608485;0;1;1;24;0;1;1;0.666666667;0;-0.666666667;-232.862855;26.9927419;1;1;-100000;0;0;0;0;9.89764482;0;4.99956307;0;9.51220425;4.99943913;0;0;0;41.0894446;11.4137346;-1.01110489;174.389019;0;1;51;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;5;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-0.666666667;-100;0;0;0;0;0;0;3000;-100000;41.0894432;4.99956322;9.897645;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;41.0530455;-100;0;4.99956287;9.89764482;0 +52;1;1;1;1;0;0;0;0;0;1;1;2;-0.666666667;5;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;-337.289735;0;1;1;24;0;1;1;0.666666667;0;-0.666666667;-232.862855;24.5746819;1;1;-100000;0;0;0;0;9.88701748;0;4.99956679;0;9.57203422;4.99945082;0;0;0;37.4529289;10.4035914;-1.00922986;158.955167;0;1;52;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;5;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-0.666666667;-100;0;0;0;0;0;0;3000;-100000;37.4529305;4.99956656;9.88701725;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;37.4165973;-100;0;4.9995666;9.88701748;0 +53;1;1;1;1;0;0;0;0;0;1;1;2;-0.666666667;5;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;-302.03361;0;1;1;24;0;1;1;0.666666667;0;-0.666666667;-232.862855;22.1609053;1;1;-100000;0;0;0;0;9.87730587;0;4.99957039;0;9.62588631;4.99946171;0;0;0;33.8228423;9.39523398;-1.00753116;143.548601;0;1;53;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;5;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-0.666666667;-100;0;0;0;0;0;0;3000;-100000;33.8228416;4.99957037;9.87730598;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;33.7865718;-100;0;4.9995702;9.87730587;0 +54;1;1;1;1;0;0;0;0;0;1;1;2;-0.666666667;5;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;-266.833962;0;1;1;24;0;1;1;0.666666667;0;-0.666666667;-232.862855;19.7509912;1;1;-100000;0;0;0;0;9.8689535;0;4.99957389;0;9.67081948;4.99947184;0;0;0;30.1985524;8.38848677;-1.00600761;128.166637;0;1;54;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;5;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-0.666666667;-100;0;0;0;0;0;0;3000;-100000;30.1985531;4.99957371;9.8689537;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;30.1623366;-100;0;4.99957371;9.8689535;0 +55;1;1;1;1;0;0;0;0;0;1;1;2;-0.666666667;5;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;-231.684686;0;1;1;24;0;1;1;0.666666667;0;-0.666666667;-232.862855;17.3445217;1;1;-100000;0;0;0;0;9.862077;0;4.99957729;0;9.70615119;4.99948127;0;0;0;26.5794303;7.38317509;-1.00465818;112.806606;0;1;55;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;5;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-0.666666667;-100;0;0;0;0;0;0;3000;-100000;26.5794296;4.99957752;9.86207676;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;26.5432631;-100;0;4.99957712;9.862077;0 +56;1;1;1;1;0;0;0;0;0;1;1;2;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;-1.0280495e-09;16.807054;0;0;-4.41482822e-07;0;0;0;0;9.85636903;0;4.99958059;0;9.73388096;4.99949005;0;0;0;25.2753205;7.02092237;-0.0238101707;107.271792;0;1;56;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-2.94477705e-12;-4.41482822e-10;0;0;0;0;0;0;3000;-4.41482825e-07;25.2753201;4.99958038;9.85636902;0;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;25.2744634;-4.41482822e-10;0;4.99958043;9.85636903;0 +57;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;16.7003381;0;0;0;0;0;0;0;9.84986206;1;4.60368761;0.632952869;9.55062319;4.99784832;14038.9043;0;0;25.1052871;6.97369085;-0.157983073;106.550148;0;1;57;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-1.17323979e-40;0;0;0;0;0;0;0;3000;0;25.1052876;4.60368776;9.8498621;0.632952869;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;25.099477;0;14.0389043;4.60385588;9.84986206;0.632952869 +58;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;15.9975197;0;0;0;0;0;0;0;9.83958503;1;4.19204189;1.55544405;9.48173857;4.99784832;41299.6073;0;0;24.0289685;6.67471346;-0.425769204;101.98211;0;1;58;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.67434913e-69;0;0;0;0;0;0;0;3000;0;24.0289688;4.19204187;9.8395853;1.555444;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;24.0135495;0;41.2996073;4.19230075;9.83958503;1.55544405 +59;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;14.6846032;0;0;0;0;0;0;0;9.82867093;1;3.82876852;2.4298787;9.42357595;4.99784832;67140.1832;0;0;22.0362037;6.12116771;-0.678522456;93.5245534;0;1;59;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-1.86232516e-97;0;0;0;0;0;0;0;3000;0;22.0362034;3.82876849;9.8286705;2.42987871;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;22.0116862;0;67.1401832;3.82898555;9.82867093;2.4298787 +60;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;12.7685006;0;0;0;0;0;0;0;9.8172304;1;3.55164274;3.29962387;9.36948843;4.99784832;92842.1795;0;0;19.1363859;5.31566276;-0.92973422;81.2173444;0;1;60;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-7.41976028e-126;0;0;0;0;0;0;0;3000;0;19.1363869;3.55164266;9.81723022;3.29962397;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;19.1028253;0;92.8421795;3.55179539;9.8172304;3.29962387 +61;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;10.3302013;0;0;0;0;0;0;0;9.80551458;1;3.38910355;3.70392686;9.38135635;4.99784832;104789.81;0;0;15.4606513;4.29462536;-1.04854645;65.617042;0;1;61;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-2.95613482e-154;0;0;0;0;0;0;0;3000;0;15.4606514;3.38910365;9.80551434;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;15.4229039;0;104.78981;3.3891804;9.80551458;3.70392686 +62;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;7.82112531;0;0;0;0;0;0;0;9.79453478;1;3.32912507;3.70392686;9.44393922;4.99784832;104789.81;0;0;11.6871651;3.24643475;-1.0478727;49.6018692;0;1;62;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-1.17776488e-182;0;0;0;0;0;0;0;3000;0;11.6871653;3.32912517;9.79453468;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;11.6494419;0;104.78981;3.32914477;9.79453478;3.70392686 +63;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;5.31344483;0;0;0;0;0;0;0;9.78445453;1;3.31561223;3.70392686;9.50065283;4.99784832;104789.81;0;0;7.9157643;2.19882342;-1.04738596;33.5955471;0;1;63;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.69237773e-211;0;0;0;0;0;0;0;3000;0;7.91576433;3.31561232;9.78445435;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;7.87805854;0;104.78981;3.31561761;9.78445453;3.70392686 +64;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;2.80671272;0;0;0;0;0;0;0;9.77539993;1;3.31055698;3.70392686;9.55064201;4.99784832;104789.81;0;0;4.14577637;1.15160455;-1.04708586;17.5952214;0;1;64;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-1.869508e-239;0;0;0;0;0;0;0;3000;0;4.14577627;3.31055689;9.77540016;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;4.10808135;0;104.78981;3.31055937;9.77539993;3.70392686 +65;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0.300482479;0;0;0;0;0;0;0;9.76787861;1;3.3079357;3.70392686;9.59061558;4.99784832;104789.81;0;0;0.376529799;0.104591611;-1.0469722;1.5980421;0;1;65;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-7.44837771e-268;0;0;0;0;0;0;0;3000;0;0.376529813;3.30793571;9.76787853;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0.338838806;0;104.78981;3.30793698;9.76787861;3.70392686 +66;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;-1.0365435e-293;1.67640739e-296;0;0;-4.4513046e-291;0;0;0;0;9.76165439;1;3.30640161;3.70392686;9.62206663;4.99784832;104789.81;0;0;0;0;0;0;0;1;66;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-2.9675364e-296;-4.4513046e-294;0;0;0;0;0;0;3000;0;0;3.30640173;9.76165485;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;-4.4513046e-294;104.78981;3.30640241;9.76165439;3.70392686 +67;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.75645902;1;3.30535474;3.70392686;9.64675987;4.99784832;104789.81;0;0;0;0;0;0;0;1;67;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30535483;9.75645924;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30535528;9.75645902;3.70392686 +68;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.75208157;1;3.30459502;3.70392686;9.66609415;4.99784832;104789.81;0;0;0;0;0;0;0;1;68;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30459499;9.75208187;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30459541;9.75208157;3.70392686 +69;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.74835371;1;3.30401881;3.70392686;9.6811783;4.99784832;104789.81;0;0;0;0;0;0;0;1;69;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30401874;9.74835396;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.3040191;9.74835371;3.70392686 +70;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.74514152;1;3.30356692;3.70392686;9.69289143;4.99784832;104789.81;0;0;0;0;0;0;0;1;70;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30356693;9.74514198;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30356714;9.74514152;3.70392686 +71;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.74233859;1;3.3032031;3.70392686;9.7019306;4.99784832;104789.81;0;0;0;0;0;0;0;1;71;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30320311;9.74233818;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30320327;9.74233859;3.70392686 +72;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.73986042;1;3.30290394;3.70392686;9.70884856;4.99784832;104789.81;0;0;0;0;0;0;0;1;72;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30290389;9.73986053;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30290407;9.73986042;3.70392686 +73;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.73763995;1;3.30265364;3.70392686;9.71408371;4.99784832;104789.81;0;0;0;0;0;0;0;1;73;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30265355;9.73764038;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30265374;9.73763995;3.70392686 +74;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.73562395;1;3.30244114;3.70392686;9.71798378;4.99784832;104789.81;0;0;0;0;0;0;0;1;74;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30244112;9.73562431;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30244122;9.73562395;3.70392686 +75;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.73377019;1;3.3022585;3.70392686;9.72082459;4.99784832;104789.81;0;0;0;0;0;0;0;1;75;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30225849;9.73377037;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30225856;9.73377019;3.70392686 +76;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.73204516;1;3.30209985;3.70392686;9.72282495;4.99784832;104789.81;0;0;0;0;0;0;0;1;76;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30209994;9.73204517;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30209989;9.73204516;3.70392686 +77;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.73042225;1;3.30196075;3.70392686;9.72415843;4.99784832;104789.81;0;0;0;0;0;0;0;1;77;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30196071;9.73042202;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30196078;9.73042225;3.70392686 +78;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.72888038;1;3.30183781;3.70392686;9.72496276;4.99784832;104789.81;0;0;0;0;0;0;0;1;78;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30183792;9.72887993;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30183783;9.72888038;3.70392686 +79;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.7274028;1;3.30172837;3.70392686;9.72534719;4.99784832;104789.81;0;0;0;0;0;0;0;1;79;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30172849;9.72740269;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30172838;9.7274028;3.70392686 +80;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.72597622;1;3.30163032;3.70392686;9.72539842;4.99784832;104789.81;0;0;0;0;0;0;0;1;80;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30163026;9.72597599;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30163033;9.72597622;3.70392686 +81;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.72458428;1;3.30154198;3.70392686;9.72522484;4.99784832;104789.81;0;0;0;0;0;0;0;1;81;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30154204;9.72458458;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30154198;9.72458428;3.70392686 +82;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.72319558;1;3.30146196;3.70392686;9.72503034;4.99784832;104789.81;0;0;0;0;0;0;0;1;82;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30146194;9.72319603;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30146196;9.72319558;3.70392686 +83;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.72180675;1;3.30138916;3.70392686;9.72483584;4.99784832;104789.81;0;0;0;0;0;0;0;1;83;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30138922;9.72180653;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30138915;9.72180675;3.70392686 +84;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.72041782;1;3.30132263;3.70392686;9.72464135;4.99784832;104789.81;0;0;0;0;0;0;0;1;84;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.3013227;9.72041798;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30132261;9.72041782;3.70392686 +85;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.71902879;1;3.30126159;3.70392686;9.72444686;4.99784832;104789.81;0;0;0;0;0;0;0;1;85;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30126166;9.71902847;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30126158;9.71902879;3.70392686 +86;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.71763968;1;3.30120541;3.70392686;9.72425237;4.99784832;104789.81;0;0;0;0;0;0;0;1;86;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.3012054;9.71763992;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30120539;9.71763968;3.70392686 +87;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.7162505;1;3.30115351;3.70392686;9.72405789;4.99784832;104789.81;0;0;0;0;0;0;0;1;87;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30115342;9.71625042;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30115349;9.7162505;3.70392686 +88;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.71486127;1;3.30110543;3.70392686;9.72386341;4.99784832;104789.81;0;0;0;0;0;0;0;1;88;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.3011055;9.71486092;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30110541;9.71486127;3.70392686 +89;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.71347198;1;3.30106076;3.70392686;9.72366893;4.99784832;104789.81;0;0;0;0;0;0;0;1;89;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30106068;9.71347237;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30106074;9.71347198;3.70392686 +90;1;1;1;1;0;0;0;0;0;1;1;2;0;15;9;10;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.71208265;1;3.30101915;3.70392686;9.72347446;4.99784832;104789.81;0;0;0;0;0;0;0;1;90;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;10;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;3.30101919;9.71208286;3.7039268;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;104.78981;3.30101913;9.71208265;3.70392686 +91;1;1;1;1;0;0;0;0;0;1;1;2;0;15;0;1;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;1;0;1;0;1;1;3000;0;0;0;0;0;0;1;22;0;1;1;24;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;9.68171099;0;4.76071709;3.31349682;9.72327999;4.99784832;93252.1416;0;0;0;0;0;0;0;1;91;0;0;0;0;0;21;0;0;0;0;0;0;3000;0.3;0.3;1;1;0;0;1;2;15;1;0;1;0;0;0;0;0;0;0;1;1;0;1;200;0;-4.94065646e-324;0;0;0;0;0;0;0;3000;0;0;4.76071692;9.6817112;3.31349683;1;0.300000012;1;0;1;1;0;1;1;0;1;1;0;1;1;0;0;93.2521416;4.76006368;9.68171099;3.31349682 diff --git a/tools/vehicle_graph/examples/vectron.script b/tools/vehicle_graph/examples/vectron.script new file mode 100644 index 00000000..c0109c43 --- /dev/null +++ b/tools/vehicle_graph/examples/vectron.script @@ -0,0 +1,25 @@ +# events for: -graph-run vectron.vhg -script vectron.script -state ready +# options: -state ready -every 100 +# start up from the cab, release the spring brake, drive off with half of the power, brake electrically, then stop +# with the train brake +0.0 toggle bat +0.2 toggle sb_release +1.0 press pant_raise +1.3 release pant_raise +1.5 press ca_reset +1.7 release ca_reset +2.0 press shp_reset +2.2 release shp_reset +17.0 inc main_sw +17.5 release main_sw +29.0 inc reverser +30.0 set lever 23 +40.0 set lever 15 +45.0 set lever 5 +50.0 press ca_reset +50.3 release ca_reset +55.0 set lever 15 +56.0 value brake 9 +75.0 press ca_reset +75.3 release ca_reset +90.0 value brake 0 diff --git a/tools/vehicle_graph/examples/vectron.vhg b/tools/vehicle_graph/examples/vectron.vhg new file mode 100644 index 00000000..a20661b4 --- /dev/null +++ b/tools/vehicle_graph/examples/vectron.vhg @@ -0,0 +1,254 @@ +// electric locomotive Vectron, built from its .fiz: the whole vehicle defined by the graph. +// induction motors with inverters (drive.async with the Engine: parameters of the .fiz), EStED distributor with the +// electrodynamic brake blending, MHZ_K8P driver's brake valve, FD1 independent brake valve, spring brake, +// vigilance device with separate acknowledgement of the cab signal. +// the submodel names of the cab and the sound files are placeholders, to be replaced with the ones of the cab model +{ + "format": "maszyna-vehicle-graph", + "version": 2, + "vehicle": { "name": "Vectron" }, + "settings": { "low_voltage_simulation": false }, + "body": { + "category": "train", "mass": 90000, "mass_rotating": 12000, "vmax": 160, "power": 5200, "sand_capacity": 700, + "length": 18.925, "height": 3.95, "width": 2.95, "cx": 0.1, + "wheel_diameter": 1.25, "wheel_inertia": 4000, "track_gauge": 1.435, "axle_arrangement": "Bo'Bo'", "axle_distance": 3, "bogie_distance": 9.5, "min_radius": 80, + "coupler_type": "Screw", "coupler_kb": 9200, "coupler_dmax_b": 0.15, "coupler_fmax_b": 740, "coupler_kc": 4800, "coupler_dmax_c": 0.15, "coupler_fmax_c": 780, + "coupler_beta": 0.58, "coupler_allowed_flag": 103, + "max_load": 7, "load_unit": "pieces", "load_accepted": "pantstate", "load_speed": 1, "unload_speed": 0.1, + "light_color": "255 255 228", + "collector_type": "WBL85", "collector_max_voltage": 4200, "collector_max_current": 2000, "collector_min_h": 0.38, "collector_max_h": 2.4, "collector_csw": 1.4, + "collector_min_voltage": 1900, "collector_layout": 3 + }, + "profiles": { "brake_pipe": 1, "feed_pipe": 1, "heating": 1 }, + "nodes": [ + // cab controls + { "id": "bat", "type": "cab.switch", "title": "Battery", "params": { "behaviour": "bistable", "submodel": "battery_sw", "command": "batterytoggle" } }, + { "id": "pant_sel", "type": "cab.switch", "title": "Pantograph selector", "params": { "behaviour": "multi", "positions": 4, "min": 0, "max": 3, "default": 1, "labels": "rear;front;both;rear", "step": 30, "submodel": "pantselect_sw", "key_inc": "pantographselectnext", "key_dec": "pantographselectprevious" } }, + { "id": "pant_raise", "type": "cab.switch", "title": "Raise pantographs", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "pantselected_sw", "command": "pantographraiseselected" } }, + { "id": "pant_lower", "type": "cab.switch", "title": "Lower pantographs", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "pantselectedoff_sw", "command": "pantographlowerselected" } }, + { "id": "main_sw", "type": "cab.switch", "title": "Main switch", "params": { "behaviour": "multi", "positions": 3, "min": -1, "max": 1, "default": 0, "spring": true, "labels": "off;0;on", "step": 40, "offset": -40, "submodel": "main_sw", "key_inc": "linebreakerclose", "key_dec": "linebreakeropen" } }, + { "id": "reverser", "type": "cab.switch", "title": "Direction", "params": { "behaviour": "multi", "positions": 3, "min": -1, "max": 1, "default": 0, "labels": "back;0;forward", "step": 45, "offset": -45, "submodel": "dirkey", "key_inc": "reverserincrease", "key_dec": "reverserdecrease" } }, + { "id": "lever", "type": "cab.switch", "title": "Power/brake lever", "params": { "behaviour": "multi", "positions": 31, "min": -1, "max": 1, "default": 0, "step": 2, "offset": -30, "submodel": "jointctrl", "key_inc": "mastercontrollerincrease", "key_dec": "mastercontrollerdecrease" } }, + { "id": "brake", "type": "cab.switch", "title": "Driver's brake valve (MHZ_K8P)", "params": { "behaviour": "multi", "positions": 12, "min": -1, "max": 10, "default": 0, "step": 8, "offset": -8, "submodel": "brakectrl", "key_inc": "trainbrakeincrease", "key_dec": "trainbrakedecrease" } }, + { "id": "local", "type": "cab.switch", "title": "Independent brake (FD1)", "params": { "behaviour": "multi", "positions": 11, "min": 0, "max": 1, "default": 0, "step": 9, "submodel": "localbrake", "key_inc": "independentbrakeincrease", "key_dec": "independentbrakedecrease" } }, + { "id": "sb_release", "type": "cab.switch", "title": "Spring brake released", "params": { "behaviour": "bistable", "submodel": "springbraketoggle_bt", "command": "springbraketoggle" } }, + { "id": "ca_reset", "type": "cab.switch", "title": "Vigilance reset", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "security_reset_bt", "command": "alerteracknowledge" } }, + { "id": "shp_reset", "type": "cab.switch", "title": "Cab signal reset", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "shp_reset_bt", "command": "cabsignalacknowledge" } }, + { "id": "sand_bt", "type": "cab.switch", "title": "Sander", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "sand_bt", "command": "sandboxactivate" } }, + { "id": "horn_lo", "type": "cab.switch", "title": "Horn low", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "hornlow_bt", "command": "hornlowactivate" } }, + { "id": "horn_hi", "type": "cab.switch", "title": "Horn high", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "hornhigh_bt", "command": "hornhighactivate" } }, + { "id": "lights_sw", "type": "cab.switch", "title": "Head lights", "params": { "behaviour": "bistable", "submodel": "lights_sw", "command": "headlighttoggleupper" } }, + { "id": "heat_sw", "type": "cab.switch", "title": "Train heating", "params": { "behaviour": "bistable", "submodel": "trainheating_sw", "command": "heatingtoggle" } }, + + // pantographs: the selector chooses them, the buttons raise and lower the selection + { "id": "pant_cmd_on", "type": "logic.and", "title": "Raising with battery" }, + { "id": "no_lower", "type": "logic.not", "title": "Lower button released" }, + { "id": "pant_cmd_off", "type": "logic.nand", "title": "Lowering or battery off" }, + { "id": "pant_cmd", "type": "logic.sr", "title": "Pantographs commanded up" }, + { "id": "sel_front", "type": "logic.compare", "title": "Front selected (1)", "params": { "op": "eq", "value": 1 } }, + { "id": "sel_both", "type": "logic.compare", "title": "Both selected (2)", "params": { "op": "eq", "value": 2 } }, + { "id": "sel_front_any", "type": "logic.or", "title": "Front in the selection" }, + { "id": "sel_rear", "type": "logic.compare", "title": "Rear in the selection", "params": { "op": "ne", "value": 1 } }, + { "id": "pant_f_up", "type": "logic.and", "title": "Front pantograph valve" }, + { "id": "pant_r_up", "type": "logic.and", "title": "Rear pantograph valve" }, + { "id": "pf", "type": "el.pantograph", "title": "Front pantograph", "params": { "index": 0, "raise_time": 5, "lower_time": 1, "min_pressure": 3.5 } }, + { "id": "pr", "type": "el.pantograph", "title": "Rear pantograph", "params": { "index": 1, "raise_time": 5, "lower_time": 1, "min_pressure": 3.5 } }, + { "id": "hv", "type": "el.junction", "title": "Roof line" }, + + // main circuit + { "id": "ms_on_req", "type": "logic.compare", "title": "Main switch to on", "params": { "op": "gt", "value": 0 } }, + { "id": "ms_off_req", "type": "logic.compare", "title": "Main switch to off", "params": { "op": "lt", "value": 0 } }, + { "id": "ms_close", "type": "logic.and", "title": "Closing with battery" }, + { "id": "ms", "type": "el.hsb", "title": "Main circuit breaker", "params": { "trip_current": 3000, "close_time": 0.2, "min_voltage": 1900, "init_time": 10 } }, + { "id": "conv", "type": "el.converter", "title": "Auxiliary converter", "params": { "power": 120000, "start_time": 10, "min_voltage": 1900, "out_voltage": 400 } }, + { "id": "batt", "type": "el.battery", "title": "Battery", "params": { "voltage": 24, "capacity": 200, "min_voltage": 18 } }, + { "id": "heat", "type": "if.heat", "title": "Train heating", "params": { "side": "rear" } }, + + // drive + { "id": "dir_set", "type": "logic.compare", "title": "Direction selected", "params": { "op": "ne", "value": 0 } }, + { "id": "drive_ok", "type": "logic.and", "title": "Ready to drive" }, + { "id": "demand", "type": "lua.block", "title": "Power/brake demand", "params": { "lua.ramp_up": 0.5, "lua.ramp_down": 1.0 }, "lua": "-- combined power/brake lever of the Vectron: forward of zero sets the traction demand, back of zero the\n-- electrodynamic brake demand. both follow the lever with limited rates, and drop to zero when the locomotive\n-- isn't ready to drive (no direction selected or main circuit open)\nlocal function approach(value, target, up, down, dt)\n if target > value then\n return math.min(target, value + up * dt)\n end\n return math.max(target, value - down * dt)\nend\n\nblock \"power_brake\" {\n inputs = { lever = \"float\", ready = \"bool\" },\n outputs = { traction = \"float\", ed_brake = \"float\" },\n params = { ramp_up = 0.5, ramp_down = 1.0 }, -- per second\n state = { t = 0.0, b = 0.0 },\n\n update = function(self, i, o, dt)\n local want_t, want_b = 0.0, 0.0\n if i.ready then\n want_t = math.max(i.lever, 0.0)\n want_b = math.max(-i.lever, 0.0)\n end\n self.t = approach(self.t, want_t, self.ramp_up, self.ramp_down, dt)\n self.b = approach(self.b, want_b, self.ramp_up, self.ramp_down, dt)\n o.traction = self.t\n o.ed_brake = self.b\n end,\n}\n" }, + { "id": "set_force", "type": "logic.math", "title": "Traction minus electric brake", "params": { "op": "sub" } }, + { "id": "motors", "type": "drive.async", "title": "Inverters and induction motors", "params": { + "dfic": 931, "dfmax": 1.67, "p": 2, "cfu": 29, "cim": 8.86, "icif": 0.642, "uzmax": 2216, "uzh": 2216, "du": 20, "i0": 20, "fcfu": 30, + "f0": 340, "a1": 0.35, "pmax": 5200, "fh": 150, "ph": 5200, "vh0": 5, "vh1": 10, "imax": 2500, "abed": 1, "edep": 1.5, "flat": true, + "motors": 4, "ratio": 4.7, "eff": 1.0 } }, + { "id": "gear", "type": "drive.gear", "title": "Gear 10:47", "params": { "ratio": 4.7, "eff": 1.0 } }, + { "id": "axles", "type": "drive.axle", "title": "Driven axles", "params": { "powered_axles": 4 } }, + + // air + { "id": "comp_on", "type": "logic.and", "title": "Compressor allowed" }, + { "id": "comp", "type": "pn.compressor", "title": "Compressor", "params": { "flow": 0.06, "max_pressure": 9.9, "min_voltage": 300, "power": 15000 } }, + { "id": "gov", "type": "pn.governor", "title": "Pressure governor", "params": { "min": 8.5, "max": 9.9 } }, + { "id": "res", "type": "pn.reservoir", "title": "Main reservoir", "params": { "volume": 0.8 } }, + { "id": "k8p", "type": "br.handle", "title": "MHZ_K8P", "params": { "type": "MHZ_K8P", "lock_on": 3.0, "lock_off": 4.5, "unlock_pos": -1, "emergency_cuts_off": true } }, + { "id": "pipe", "type": "br.pipe", "title": "Brake pipe" }, + { "id": "fd1", "type": "br.local_handle", "title": "FD1", "params": { "type": "FD1" } }, + { "id": "dist", "type": "br.distributor", "title": "EStED distributor", "params": { + "type": "EStED", "mode": "GPR", "max_bp": 3.7, "max_lbp": 3.7, "cyl_count": 8, "cyl_radius": 0.127, "cyl_dist": 0.04, "volume": 125, "rm": 0.81, + "delay1": 15, "delay2": 4, "delay3": 35, "delay4": 25, "asb": "automatic" } }, + { "id": "rig", "type": "br.rigging", "title": "Brake rigging (discs)", "params": { "bcm": 2.7, "bcs": 0.8, "bsa": 0, "bre": 1.0, "nbpa": 4, "material": "Disk2" } }, + { "id": "spring", "type": "pn.spring_brake", "title": "Spring brake", "params": { + "volume": 10, "mbf": 8, "max_sp": 5.5, "reset_p": 4.0, "min_fp": 3.5, "press_off": 4.5, "press_on": 3.5, "valve_on_area": 0.0002, "valve_off_area": 0.0002, + "valve_pnb_area": 0.002 } }, + { "id": "pg_front", "type": "if.pg", "title": "Brake pipe, front", "params": { "side": "front" } }, + { "id": "pg_rear", "type": "if.pg", "title": "Brake pipe, rear", "params": { "side": "rear" } }, + { "id": "pz_front", "type": "if.pz", "title": "Feed pipe, front", "params": { "side": "front", "cock": true } }, + { "id": "pz_rear", "type": "if.pz", "title": "Feed pipe, rear", "params": { "side": "rear", "cock": true } }, + + // vehicle + { "id": "vs", "type": "veh.state", "title": "Motion" }, + { "id": "ca", "type": "sec.ca", "title": "Vigilance device (CA/SHP)", "params": { + "aware": true, "cab_signal": true, "separate_ack": true, "aware_delay": 30, "sound_delay": 0, "emergency_delay": 5, "max_hold_time": 1.0, "radio_stop": true } }, + { "id": "sand", "type": "veh.sander", "title": "Sander" }, + { "id": "pc1", "type": "logic.const", "title": "Pc1: three white lights", "params": { "value": 21 } }, + { "id": "pc_front", "type": "logic.math", "title": "Front lights when switched on", "params": { "op": "mul" } }, + { "id": "lights", "type": "veh.lights", "title": "Lights" }, + + // indicators + { "id": "ms_open", "type": "logic.not", "title": "Main circuit open" }, + { "id": "ms_lamp", "type": "logic.and", "title": "Main circuit lamp powered" }, + { "id": "pant_not_up", "type": "logic.not", "title": "Pantograph not up yet" }, + { "id": "pant_moving", "type": "logic.and", "title": "Pantograph moving" }, + { "id": "sb_lamp", "type": "logic.and", "title": "Spring brake lamp powered" }, + { "id": "lamp_ms", "type": "fb.lamp", "title": "Main circuit", "params": { "color": "red", "submodel": "i-mainbreaker" } }, + { "id": "lamp_pant", "type": "fb.lamp", "title": "Pantograph moving", "params": { "color": "yellow", "blink": true, "blink_period": 1.0, "submodel": "i-pantraising" } }, + { "id": "lamp_sb", "type": "fb.lamp", "title": "Spring brake applied", "params": { "color": "red", "submodel": "i-springbrake" } }, + { "id": "lamp_ca", "type": "fb.lamp", "title": "Vigilance", "params": { "color": "white", "submodel": "i-security_aware" } }, + { "id": "lamp_shp", "type": "fb.lamp", "title": "Cab signal", "params": { "color": "white", "submodel": "i-security_cabsignal" } }, + { "id": "hv_volts", "type": "logic.math", "title": "Voltage of the roof line", "params": { "op": "max" } }, + { "id": "g_hv", "type": "fb.gauge", "title": "Catenary voltage", "params": { "min": 0, "max": 4000, "unit": "V", "angle_min": 0, "angle_max": 270, "submodel": "hvoltage" } }, + { "id": "g_force", "type": "fb.gauge", "title": "Tractive force", "params": { "min": -150000, "max": 300000, "unit": "N", "angle_min": -90, "angle_max": 180, "submodel": "tractionforce" } }, + { "id": "g_v", "type": "fb.gauge", "title": "Speedometer", "params": { "min": 0, "max": 200, "unit": "km/h", "angle_min": 0, "angle_max": 300, "submodel": "tachometer" } }, + { "id": "g_pipes", "type": "fb.manometer", "title": "Brake pipe and main reservoir", "params": { "min": 0, "max": 10, "submodel": "pipepress", "submodel2": "compressor" } }, + { "id": "g_bc", "type": "fb.gauge", "title": "Brake cylinders", "params": { "min": 0, "max": 10, "unit": "bar", "angle_min": 0, "angle_max": 270, "submodel": "cylinder" } }, + { "id": "x_v", "type": "fb.export", "title": "Speed for the screens", "params": { "name": "graph_velocity", "kind": "float" } }, + { "id": "x_force", "type": "fb.export", "title": "Force for the screens", "params": { "name": "graph_force", "kind": "float" } }, + { "id": "snd_base", "type": "logic.const", "title": "Idle volume", "params": { "value": 0.3 } }, + { "id": "snd_gain", "type": "logic.math", "title": "Volume rising with demand", "params": { "op": "add" } }, + { "id": "snd_inverter", "type": "fb.sound", "title": "Inverters", "params": { "file": "vectron-inverter.wav", "placement": "engine", "loop": true } }, + { "id": "snd_comp", "type": "fb.sound", "title": "Compressor", "params": { "file": "vectron-compressor.wav", "placement": "engine", "loop": true } }, + { "id": "snd_buzzer", "type": "fb.sound", "title": "Vigilance buzzer", "params": { "file": "buzzer.wav", "placement": "internal", "loop": true } }, + { "id": "snd_horn_lo", "type": "fb.sound", "title": "Horn low", "params": { "file": "horn1.wav", "placement": "external", "range": 1000, "loop": true } }, + { "id": "snd_horn_hi", "type": "fb.sound", "title": "Horn high", "params": { "file": "horn2.wav", "placement": "external", "range": 1000, "loop": true } } + ], + "connections": [ + { "from": "bat.pos", "to": "batt.on" }, + { "from": "conv.aux", "to": "batt.charge" }, + + { "from": "pant_raise.pos", "to": "pant_cmd_on.a" }, + { "from": "bat.pos", "to": "pant_cmd_on.b" }, + { "from": "pant_lower.pos", "to": "no_lower.a" }, + { "from": "no_lower.q", "to": "pant_cmd_off.a" }, + { "from": "bat.pos", "to": "pant_cmd_off.b" }, + { "from": "pant_cmd_on.q", "to": "pant_cmd.s" }, + { "from": "pant_cmd_off.q", "to": "pant_cmd.r" }, + { "from": "pant_sel.pos", "to": "sel_front.a" }, + { "from": "pant_sel.pos", "to": "sel_both.a" }, + { "from": "pant_sel.pos", "to": "sel_rear.a" }, + { "from": "sel_front.q", "to": "sel_front_any.a" }, + { "from": "sel_both.q", "to": "sel_front_any.b" }, + { "from": "pant_cmd.q", "to": "pant_f_up.a" }, + { "from": "sel_front_any.q", "to": "pant_f_up.b" }, + { "from": "pant_cmd.q", "to": "pant_r_up.a" }, + { "from": "sel_rear.q", "to": "pant_r_up.b" }, + { "from": "pant_f_up.q", "to": "pf.raise" }, + { "from": "pant_r_up.q", "to": "pr.raise" }, + { "from": "res.out", "to": "pf.air" }, + { "from": "res.out", "to": "pr.air" }, + { "from": "pf.wn", "to": "hv.a" }, + { "from": "pr.wn", "to": "hv.b" }, + + { "from": "main_sw.pos", "to": "ms_on_req.a" }, + { "from": "main_sw.pos", "to": "ms_off_req.a" }, + { "from": "ms_on_req.q", "to": "ms_close.a" }, + { "from": "bat.pos", "to": "ms_close.b" }, + { "from": "ms_close.q", "to": "ms.close" }, + { "from": "ms_off_req.q", "to": "ms.open" }, + { "from": "bat.pos", "to": "ms.hold" }, + { "from": "hv.out", "to": "ms.in" }, + { "from": "ms.out", "to": "conv.wn" }, + { "from": "ms.closed", "to": "conv.on" }, + { "from": "ms.out", "to": "heat.wn" }, + { "from": "heat_sw.pos", "to": "heat.on" }, + + { "from": "reverser.pos", "to": "dir_set.a" }, + { "from": "dir_set.q", "to": "drive_ok.a" }, + { "from": "ms.closed", "to": "drive_ok.b" }, + { "from": "lever.pos", "to": "demand.lever" }, + { "from": "drive_ok.q", "to": "demand.ready" }, + { "from": "demand.traction", "to": "set_force.a" }, + { "from": "demand.ed_brake", "to": "set_force.b" }, + { "from": "ms.out", "to": "motors.wn" }, + { "from": "set_force.q", "to": "motors.demand" }, + { "from": "reverser.pos", "to": "motors.dir" }, + { "from": "motors.shaft", "to": "gear.in" }, + { "from": "gear.out", "to": "axles.shaft" }, + + { "from": "conv.aux", "to": "comp.power" }, + { "from": "gov.need", "to": "comp_on.a" }, + { "from": "bat.pos", "to": "comp_on.b" }, + { "from": "comp_on.q", "to": "comp.on" }, + { "from": "comp.air", "to": "res.in" }, + { "from": "res.p", "to": "gov.p" }, + { "from": "res.out", "to": "k8p.zg" }, + { "from": "brake.pos", "to": "k8p.pos" }, + { "from": "k8p.pg", "to": "pipe.in" }, + { "from": "ca.brake", "to": "pipe.vent" }, + { "from": "res.out", "to": "fd1.zg" }, + { "from": "local.pos", "to": "fd1.pos" }, + { "from": "pipe.dist", "to": "dist.pg" }, + { "from": "res.pz", "to": "dist.feed" }, + { "from": "fd1.bc", "to": "dist.local" }, + { "from": "motors.ed", "to": "dist.ed" }, + { "from": "dist.bc", "to": "rig.bc" }, + { "from": "sb_release.pos", "to": "spring.release" }, + { "from": "res.out", "to": "spring.feed" }, + { "from": "spring.force", "to": "rig.spring" }, + { "from": "pipe.couplers", "to": "pg_front.pg" }, + { "from": "pipe.couplers", "to": "pg_rear.pg" }, + { "from": "res.pz", "to": "pz_front.pz" }, + { "from": "res.pz", "to": "pz_rear.pz" }, + + { "from": "vs.v", "to": "ca.v" }, + { "from": "batt.ok", "to": "ca.power" }, + { "from": "ca_reset.pos", "to": "ca.reset" }, + { "from": "shp_reset.pos", "to": "ca.reset_shp" }, + { "from": "sand_bt.pos", "to": "sand.on" }, + { "from": "pc1.q", "to": "pc_front.a" }, + { "from": "lights_sw.pos", "to": "pc_front.b" }, + { "from": "pc_front.q", "to": "lights.front" }, + + { "from": "ms.closed", "to": "ms_open.a" }, + { "from": "ms_open.q", "to": "ms_lamp.a" }, + { "from": "batt.ok", "to": "ms_lamp.b" }, + { "from": "ms_lamp.q", "to": "lamp_ms.on" }, + { "from": "pf.up", "to": "pant_not_up.a" }, + { "from": "pant_cmd.q", "to": "pant_moving.a" }, + { "from": "pant_not_up.q", "to": "pant_moving.b" }, + { "from": "pant_moving.q", "to": "lamp_pant.on" }, + { "from": "spring.applied", "to": "sb_lamp.a" }, + { "from": "batt.ok", "to": "sb_lamp.b" }, + { "from": "sb_lamp.q", "to": "lamp_sb.on" }, + { "from": "ca.lamp", "to": "lamp_ca.on" }, + { "from": "ca.shp_lamp", "to": "lamp_shp.on" }, + { "from": "pf.u", "to": "hv_volts.a" }, + { "from": "pr.u", "to": "hv_volts.b" }, + { "from": "hv_volts.q", "to": "g_hv.value" }, + { "from": "axles.force", "to": "g_force.value" }, + { "from": "vs.v", "to": "g_v.value" }, + { "from": "pipe.p", "to": "g_pipes.black" }, + { "from": "res.p", "to": "g_pipes.red" }, + { "from": "dist.bc", "to": "g_bc.value" }, + { "from": "vs.v", "to": "x_v.value" }, + { "from": "axles.force", "to": "x_force.value" }, + { "from": "ms.closed", "to": "snd_inverter.play" }, + { "from": "snd_base.q", "to": "snd_gain.a" }, + { "from": "demand.traction", "to": "snd_gain.b" }, + { "from": "snd_gain.q", "to": "snd_inverter.volume" }, + { "from": "comp.run", "to": "snd_comp.play" }, + { "from": "ca.buzzer", "to": "snd_buzzer.play" }, + { "from": "horn_lo.pos", "to": "snd_horn_lo.play" }, + { "from": "horn_hi.pos", "to": "snd_horn_hi.play" } + ] +} diff --git a/tools/vehicle_graph/run_tests.py b/tools/vehicle_graph/run_tests.py index c9511d04..c8762a19 100644 --- a/tools/vehicle_graph/run_tests.py +++ b/tools/vehicle_graph/run_tests.py @@ -6,7 +6,10 @@ usage: run_tests.py [--update] - every tests/*.vhg file starts with a line "// expect: ok", "// expect: error " or "// expect: warning ", and is checked against the output of -graph-run; - every .vhg with a .script, in both directories, is run twice, and both results have to be identical (determinism) - and equal to .expected.csv. --update writes the current results as the expected ones. + and equal to .expected.csv. --update writes the current results as the expected ones. A script line + "# options: ..." adds options of -graph-run, e.g. "# options: -state ready -every 100"; +- the results are compared with the expected ones with a relative tolerance of 1e-6, as the vehicle body computations may + differ in the last digits between compilers. """ import pathlib @@ -43,14 +46,47 @@ def check_file(executable, path): return None +def script_options(script): + for line in script.read_text(encoding="utf-8").splitlines(): + if line.startswith("# options:"): + return line[len("# options:"):].split() + return [] + + +def same_results(expected, actual, tolerance=1e-6): + expected_lines = expected.decode("utf-8").splitlines() + actual_lines = actual.decode("utf-8").splitlines() + if len(expected_lines) != len(actual_lines): + return False + for expected_line, actual_line in zip(expected_lines, actual_lines): + if expected_line == actual_line: + continue + expected_cells = expected_line.split(";") + actual_cells = actual_line.split(";") + if len(expected_cells) != len(actual_cells): + return False + for expected_cell, actual_cell in zip(expected_cells, actual_cells): + if expected_cell == actual_cell: + continue + try: + a = float(expected_cell) + b = float(actual_cell) + except ValueError: + return False + if abs(a - b) > tolerance * max(1.0, abs(a), abs(b)): + return False + return True + + def check_example(executable, path, update): script = path.with_suffix(".script") expected = path.with_suffix(".expected.csv") + options = script_options(script) results = [] with tempfile.TemporaryDirectory() as directory: for attempt in range(2): output = pathlib.Path(directory) / f"run{attempt}.csv" - code, log = run(executable, path, "-script", script, "-every", 10, "-o", output) + code, log = run(executable, path, "-script", script, "-every", 10, *options, "-o", output) if code != 0: return f"{path.name}: run failed:\n{log}" # git may check the expected files out with windows line endings @@ -62,7 +98,7 @@ def check_example(executable, path, update): return None if not expected.exists(): return f"{path.name}: missing {expected.name}, run with --update to create it" - if expected.read_bytes().replace(b"\r\n", b"\n") != results[0]: + if not same_results(expected.read_bytes().replace(b"\r\n", b"\n"), results[0]): return f"{path.name}: results differ from {expected.name}" return None diff --git a/tools/vehicle_graph/tests/anim.expected.csv b/tools/vehicle_graph/tests/anim.expected.csv new file mode 100644 index 00000000..567ab8b8 --- /dev/null +++ b/tools/vehicle_graph/tests/anim.expected.csv @@ -0,0 +1,51 @@ +time;x.q;on.q;blind.anim;flap.anim;quick.anim;beacon.shown;sign.shown;blind:value;blind:anim;flap:value;flap:anim;quick:value;quick:anim;beacon:on;sign:on;body:v;body:ft;body:fb;body:pg;body:zg;body:bc +0.1;0.5;0;45;210;45;0;0;0.5;45;0.5;210;0.5;45;0;0;0;0;0;0;0;0 +0.2;0.5;0;45;210;45;0;0;0.5;45;0.5;210;0.5;45;0;0;0;0;0;0;0;0 +0.3;0.5;0;45;210;45;0;0;0.5;45;0.5;210;0.5;45;0;0;0;0;0;0;0;0 +0.4;0.5;0;45;210;45;0;0;0.5;45;0.5;210;0.5;45;0;0;0;0;0;0;0;0 +0.5;0.5;0;45;210;45;0;0;0.5;45;0.5;210;0.5;45;0;0;0;0;0;0;0;0 +0.6;1;0;53.2317237;410;90;0;0;0.591463597;53.2317237;1;410;1;90;0;0;0;0;0;0;0;0 +0.7;1;0;59.9576413;410;90;0;0;0.666196014;59.9576413;1;410;1;90;0;0;0;0;0;0;0;0 +0.8;1;0;65.4532056;410;90;0;0;0.72725784;65.4532056;1;410;1;90;0;0;0;0;0;0;0;0 +0.9;1;0;69.9434818;410;90;0;0;0.777149798;69.9434818;1;410;1;90;0;0;0;0;0;0;0;0 +1;1;0;73.6123644;410;90;0;0;0.81791516;73.6123644;1;410;1;90;0;0;0;0;0;0;0;0 +1.1;1;0;76.6101086;410;90;0;0;0.851223429;76.6101086;1;410;1;90;0;0;0;0;0;0;0;0 +1.2;1;0;79.0594838;410;90;0;0;0.878438709;79.0594838;1;410;1;90;0;0;0;0;0;0;0;0 +1.3;1;0;81.0608017;410;90;0;0;0.900675575;81.0608017;1;410;1;90;0;0;0;0;0;0;0;0 +1.4;1;0;82.6960242;410;90;0;0;0.918844713;82.6960242;1;410;1;90;0;0;0;0;0;0;0;0 +1.5;1;0;84.03212;410;90;0;0;0.933690222;84.03212;1;410;1;90;0;0;0;0;0;0;0;0 +1.6;1;0;85.1238075;410;90;0;0;0.945820084;85.1238075;1;410;1;90;0;0;0;0;0;0;0;0 +1.7;1;0;86.0157957;410;90;0;0;0.955731064;86.0157957;1;410;1;90;0;0;0;0;0;0;0;0 +1.8;1;0;86.744615;410;90;0;0;0.963829056;86.744615;1;410;1;90;0;0;0;0;0;0;0;0 +1.9;1;0;87.3401135;410;90;0;0;0.970445705;87.3401135;1;410;1;90;0;0;0;0;0;0;0;0 +2;1;0;87.826679;410;90;0;0;0.975851989;87.826679;1;410;1;90;0;0;0;0;0;0;0;0 +2.1;3;0;88.2242385;410;90;0;0;0.980269317;88.2242385;1;410;1;90;0;0;0;0;0;0;0;0 +2.2;3;0;88.5490736;410;90;0;0;0.983878596;88.5490736;1;410;1;90;0;0;0;0;0;0;0;0 +2.3;3;0;88.8144875;410;90;0;0;0.986827639;88.8144875;1;410;1;90;0;0;0;0;0;0;0;0 +2.4;3;0;89.03135;410;90;0;0;0.989237222;89.03135;1;410;1;90;0;0;0;0;0;0;0;0 +2.5;3;0;89.2085424;410;90;0;0;0.991206027;89.2085424;1;410;1;90;0;0;0;0;0;0;0;0 +2.6;-1;0;72.8898741;-390;0;0;0;0.80988749;72.8898741;-1;-390;0;0;0;0;0;0;0;0;0;0 +2.7;-1;0;59.5563341;-390;0;0;0;0.661737045;59.5563341;-1;-390;0;0;0;0;0;0;0;0;0;0 +2.8;-1;0;48.661861;-390;0;0;0;0.540687345;48.661861;-1;-390;0;0;0;0;0;0;0;0;0;0 +2.9;-1;0;39.7602834;-390;0;0;0;0.441780926;39.7602834;-1;-390;0;0;0;0;0;0;0;0;0;0 +3;-1;0;32.4870463;-390;0;0;0;0.360967182;32.4870463;-1;-390;0;0;0;0;0;0;0;0;0;0 +3.1;-1;1;26.5442821;-390;0;0;1;0.294936468;26.5442821;-1;-390;0;0;0;1;0;0;0;0;0;0 +3.2;-1;1;21.6886111;-390;0;0;1;0.240984568;21.6886111;-1;-390;0;0;0;1;0;0;0;0;0;0 +3.3;-1;1;17.7211744;-390;0;1;1;0.196901937;17.7211744;-1;-390;0;0;1;1;0;0;0;0;0;0 +3.4;-1;1;14.4794897;-390;0;1;1;0.160883219;14.4794897;-1;-390;0;0;1;1;0;0;0;0;0;0 +3.5;-1;1;11.8307973;-390;0;0;1;0.131453303;11.8307973;-1;-390;0;0;0;1;0;0;0;0;0;0 +3.6;-1;1;9.66662274;-390;0;0;1;0.107406919;9.66662274;-1;-390;0;0;0;1;0;0;0;0;0;0 +3.7;-1;1;7.89833457;-390;0;1;1;0.087759273;7.89833457;-1;-390;0;0;1;1;0;0;0;0;0;0 +3.8;-1;1;6.4535144;-390;0;1;1;0.0717057156;6.4535144;-1;-390;0;0;1;1;0;0;0;0;0;0 +3.9;-1;1;5.27299112;-390;0;0;1;0.0585887903;5.27299112;-1;-390;0;0;0;1;0;0;0;0;0;0 +4;-1;1;4.30841766;-390;0;0;1;0.0478713073;4.30841766;-1;-390;0;0;0;1;0;0;0;0;0;0 +4.1;-1;0;3.52029091;-390;0;0;0;0.0391143434;3.52029091;-1;-390;0;0;0;0;0;0;0;0;0;0 +4.2;-1;0;2.87633397;-390;0;0;0;0.0319592664;2.87633397;-1;-390;0;0;0;0;0;0;0;0;0;0 +4.3;-1;0;2.35017427;-390;0;0;0;0.0261130475;2.35017427;-1;-390;0;0;0;0;0;0;0;0;0;0 +4.4;-1;0;1.92026349;-390;0;0;0;0.021336261;1.92026349;-1;-390;0;0;0;0;0;0;0;0;0;0 +4.5;-1;0;1.56899508;-390;0;0;0;0.0174332787;1.56899508;-1;-390;0;0;0;0;0;0;0;0;0;0 +4.6;-1;0;1.28198321;-390;0;0;0;0.0142442579;1.28198321;-1;-390;0;0;0;0;0;0;0;0;0;0 +4.7;-1;0;1.04747362;-390;0;0;0;0.0116385958;1.04747362;-1;-390;0;0;0;0;0;0;0;0;0;0 +4.8;-1;0;0.855862214;-390;0;0;0;0.00950958015;0.855862214;-1;-390;0;0;0;0;0;0;0;0;0;0 +4.9;-1;0;0.699301741;-390;0;0;0;0.00777001935;0.699301741;-1;-390;0;0;0;0;0;0;0;0;0;0 +5;-1;0;0.571380437;-390;0;0;0;0.00634867152;0.571380437;-1;-390;0;0;0;0;0;0;0;0;0;0 diff --git a/tools/vehicle_graph/tests/anim.script b/tools/vehicle_graph/tests/anim.script new file mode 100644 index 00000000..593c842e --- /dev/null +++ b/tools/vehicle_graph/tests/anim.script @@ -0,0 +1,9 @@ +# events for: -graph-run anim.vhg -script anim.script +# the first step starts at the input (45 deg), then the blind follows it with the smoothing of TGauge (0.5 s); +# inputs outside min..max are clamped; smoothing shorter than two steps jumps, like TGauge +0.0 force x.q 0.5 +0.5 force x.q 1 +2.0 force x.q 3 +2.5 force x.q -1 +3.0 force on.q 1 +4.0 force on.q 0 diff --git a/tools/vehicle_graph/tests/anim.vhg b/tools/vehicle_graph/tests/anim.vhg new file mode 100644 index 00000000..f9469c2c --- /dev/null +++ b/tools/vehicle_graph/tests/anim.vhg @@ -0,0 +1,19 @@ +// expect: ok +// blocks of the vehicle's external model (veh.anim, veh.visible); without models only their state is computed. see anim.script +{ + "format": "maszyna-vehicle-graph", "version": 2, + "body": { "mass": 20000, "length": 14, "wheel_diameter": 0.9, "axle_arrangement": "2'2'", "vmax": 100 }, + "nodes": [ + { "id": "x", "type": "logic.const", "params": { "value": 0 } }, + { "id": "on", "type": "logic.const", "params": { "value": 0 } }, + { "id": "blind", "type": "veh.anim", "params": { "submodel": "roleta", "animation": "rotate", "scale": 90, "smoothing": 0.5 } }, + { "id": "flap", "type": "veh.anim", "params": { "model": "lowpoly", "submodel": "klapa", "animation": "move", "min": -1, "max": 1, "scale": 400, "offset": 10, "smoothing": 0 } }, + { "id": "quick", "type": "veh.anim", "params": { "submodel": "x", "smoothing": 0.015 } }, + { "id": "beacon", "type": "veh.visible", "params": { "submodel": "lampa_on", "submodel_off": "lampa_off", "blink": true, "blink_period": 0.4 } }, + { "id": "sign", "type": "veh.visible", "params": { "submodel": "tablica" } } + ], + "connections": [ + { "from": "x.q", "to": "blind.value" }, { "from": "x.q", "to": "flap.value" }, { "from": "x.q", "to": "quick.value" }, + { "from": "on.q", "to": "beacon.on" }, { "from": "on.q", "to": "sign.on" } + ] +} diff --git a/tools/vehicle_graph/tests/body-type.vhg b/tools/vehicle_graph/tests/body-type.vhg new file mode 100644 index 00000000..5a56efed --- /dev/null +++ b/tools/vehicle_graph/tests/body-type.vhg @@ -0,0 +1,2 @@ +// expect: error body parameter "mass" has to be a number +{ "format": "maszyna-vehicle-graph", "version": 2, "body": { "mass": "heavy" }, "nodes": [ { "id": "c", "type": "logic.const" } ] } diff --git a/tools/vehicle_graph/tests/body-unknown.vhg b/tools/vehicle_graph/tests/body-unknown.vhg new file mode 100644 index 00000000..21ce0b2e --- /dev/null +++ b/tools/vehicle_graph/tests/body-unknown.vhg @@ -0,0 +1,2 @@ +// expect: warning unknown body parameter "weight" was ignored +{ "format": "maszyna-vehicle-graph", "version": 2, "body": { "mass": 1000, "weight": 3 }, "nodes": [ { "id": "c", "type": "logic.const" } ] } diff --git a/tools/vehicle_graph/tests/brakes.expected.csv b/tools/vehicle_graph/tests/brakes.expected.csv new file mode 100644 index 00000000..01168f3b --- /dev/null +++ b/tools/vehicle_graph/tests/brakes.expected.csv @@ -0,0 +1,172 @@ +time;brake.pos;brake.index;on.q;comp.run;gov.need;comp_on.q;res.p;fv4a.p_ctl;pipe.p;dist.bc;dist.aux;dist.crp;rig.force;brake:position;gov:need;body:v;body:ft;body:fb;body:pg;body:zg;body:bc +1;0;2;1;1;1;1;4.22222977;0;3.56101118;3.98128717;7.41978569;4.99300468;151590.604;2;1;0;0;151.590604;3.56100671;4.22222977;3.98128717 +2;0;2;1;1;1;1;4.31706891;0;3.62579742;3.97977334;7.41797629;4.99300468;151905.876;2;1;0;0;151.905876;3.62578931;4.31706891;3.97977334 +3;0;2;1;1;1;1;4.41064336;0;3.69184587;3.9189975;7.41785266;4.99300468;151905.876;2;1;0;0;151.905876;3.69183341;4.41064336;3.9189975 +4;0;2;1;1;1;1;4.50299655;0;3.7594508;3.84771183;7.41772903;4.99300468;151905.876;2;1;0;0;151.905876;3.75943696;4.50299655;3.84771183 +5;0;2;1;1;1;1;4.59417112;0;3.82853191;3.76743508;7.4176054;4.99300468;151905.876;2;1;0;0;151.905876;3.82851686;4.59417112;3.76743508 +6;0;2;1;1;1;1;4.68420675;0;3.89898835;3.6808666;7.41748177;4.99300468;151905.876;2;1;0;0;151.905876;3.89897221;4.68420675;3.6808666 +7;0;2;1;1;1;1;4.77314062;0;3.97072381;3.58583282;7.41735815;4.99300468;151905.876;2;1;0;0;151.905876;3.97070667;4.77314062;3.58583282 +8;0;2;1;1;1;1;4.86100775;0;4.04364668;3.48536018;7.41723453;4.99300468;151905.876;2;1;0;0;151.905876;4.04362862;4.86100775;3.48536018 +9;0;2;1;1;1;1;4.94784129;0;4.11766984;3.37928308;7.41711091;4.99300468;151905.876;2;1;0;0;151.905876;4.11765091;4.94784129;3.37928308 +10;0;2;1;1;1;1;5.03367277;0;4.19271026;3.267657;7.41698729;4.99300468;151905.876;2;1;0;0;151.905876;4.19269052;5.03367277;3.267657 +11;0;2;1;1;1;1;5.11853225;0;4.26868867;3.15078215;7.41686368;4.99300468;151905.876;2;1;0;0;151.905876;4.26866816;5.11853225;3.15078215 +12;0;2;1;1;1;1;5.20244852;0;4.34552919;3.0289181;7.41674006;4.99300468;151905.876;2;1;0;0;151.905876;4.34550793;5.20244852;3.0289181 +13;0;2;1;1;1;1;5.28544921;0;4.423159;2.90229356;7.41661645;4.99300468;147615.017;2;1;0;0;147.615017;4.42313705;5.28544921;2.90229356 +14;0;2;1;1;1;1;5.36756088;0;4.50150813;2.7711135;7.41649284;4.99300468;141119.213;2;1;0;0;141.119213;4.50148551;5.36756088;2.7711135 +15;0;2;1;1;1;1;5.44880914;0;4.58050921;2.63556444;7.41636923;4.99300468;134370.003;2;1;0;0;134.370003;4.58048594;5.44880914;2.63556444 +16;0;2;1;1;1;1;5.53031967;0;4.65574966;2.49589806;7.41624563;4.99300468;127373.568;2;1;0;0;127.373568;4.65572631;5.53031967;2.49589806 +17;0;2;1;1;1;1;5.61446747;0;4.71827224;2.35330799;7.41612203;4.99300468;120183.263;2;1;0;0;120.183263;4.71824915;5.61446747;2.35330799 +18;0;2;1;1;1;1;5.70072793;0;4.77006834;2.20939661;7.41599843;4.99300468;112874.213;2;1;0;0;112.874213;4.77004562;5.70072793;2.20939661 +19;0;2;1;1;1;1;5.78860133;0;4.81299587;2.06545058;7.41587483;4.99300468;105507.212;2;1;0;0;105.507212;4.81297361;5.78860133;2.06545058 +20;0;2;1;1;1;1;5.87769947;0;4.84850035;1.92252558;7.41575123;4.99300468;98132.8605;2;1;0;0;98.1328605;4.84847862;5.87769947;1.92252558 +21;0;2;1;1;1;1;5.9677094;0;4.87775073;1.78151079;7.41562763;4.99300468;90794.7487;2;1;0;0;90.7947487;4.8777296;5.9677094;1.78151079 +22;0;2;1;1;1;1;6.05837091;0;4.9017228;1.64316858;7.41550404;4.99300468;83531.414;2;1;0;0;83.531414;4.90170232;6.05837091;1.64316858 +23;0;2;1;1;1;1;6.14946302;0;4.9212496;1.50815959;7.41538045;4.99300468;76377.5779;2;1;0;0;76.3775779;4.92122983;6.14946302;1.50815959 +24;0;2;1;1;1;1;6.24079578;0;4.93705145;1.37705951;7.41525686;4.99300468;69364.9442;2;1;0;0;69.3649442;4.93703243;6.24079578;1.37705951 +25;0;2;1;1;1;1;6.33220531;0;4.94975392;1.25037059;7.41513328;4.99300468;62522.7234;2;1;0;0;62.5227234;4.94973569;6.33220531;1.25037059 +26;0;2;1;1;1;1;6.42355072;0;4.95989912;1.12853024;7.41500969;4.99300468;55877.9835;2;1;0;0;55.8779835;4.95988172;6.42355072;1.12853024 +27;0;2;1;1;1;1;6.51471178;0;4.96795373;1.01191777;7.41488611;4.99300468;49455.8857;2;1;0;0;49.4558857;4.9679372;6.51471178;1.01191777 +28;0;2;1;1;1;1;6.60558704;0;4.97431564;0.900873349;7.41476253;4.99300468;43280.5888;2;1;0;0;43.2805888;4.97429999;6.60558704;0.900873349 +29;0;2;1;1;1;1;6.6960921;0;4.97932022;0.795708934;7.41463895;4.99300468;37375.7284;2;1;0;0;37.3757284;4.97930547;6.6960921;0.795708934 +30;0;2;1;1;1;1;6.78615776;0;4.98324672;0.696670616;7.41451537;4.99300468;31762.1371;2;1;0;0;31.7621371;4.9832329;6.78615776;0.696670616 +31;0;2;1;1;1;1;6.87572826;0;4.98632476;0.603964137;7.4143918;4.99300468;26459.0493;2;1;0;0;26.4590493;4.98631189;6.87572826;0.603964137 +32;0;2;1;1;1;1;6.96475945;0;4.98874076;0.517759076;7.41426823;4.99300468;21484.1344;2;1;0;0;21.4841344;4.98872885;6.96475945;0.517759076 +33;0;2;1;1;1;1;7.05321272;0;4.99054046;0.434090415;7.41414466;4.99113644;16613.7287;2;1;0;0;16.6137287;4.99052829;7.05321272;0.434090415 +34;0;2;1;1;1;1;7.14105877;0;4.99198306;0.356648401;7.41402109;4.98938876;12067.6869;2;1;0;0;12.0676869;4.99197204;7.14105877;0.356648401 +35;0;2;1;1;1;1;7.2282826;0;4.99323044;0.290226821;7.41389752;4.98798689;8138.42397;2;1;0;0;8.13842397;4.99322421;7.2282826;0.290226821 +36;0;2;1;1;1;1;7.31489569;0;4.99460728;0.257609809;7.41377396;4.987756;6198.46475;2;1;0;0;6.19846475;4.9946039;7.31489569;0.257609809 +37;0;2;1;1;1;1;7.40090042;0;4.99569931;0.234806519;7.4136504;4.9882132;4838.03372;2;1;0;0;4.83803372;4.99569636;7.40090042;0.234806519 +38;0;2;1;1;1;1;7.48550947;0;4.99651956;0.214667355;7.41775132;4.98882183;3633.66403;2;1;0;0;3.63366403;4.99651695;7.48550947;0.214667355 +39;0;2;1;1;1;1;7.56749057;0;4.99713455;0.196487922;7.43264079;4.98950205;2544.15155;2;1;0;0;2.54415155;4.99713223;7.56749057;0.196487922 +40;0;2;1;1;1;1;7.64714329;0;4.99759659;0.180038275;7.456722;4.99021499;1556.37905;2;1;0;0;1.55637905;4.99759451;7.64714329;0.180038275 +41;0;2;1;1;1;1;7.72475402;0;4.99794536;0.165139537;7.48848972;4.99093688;660.143874;2;1;0;0;0.660143874;4.99794349;7.72475402;0.165139537 +42;0;2;1;1;1;1;7.80063264;0;4.9982105;0.151637352;7.52630483;4.99165181;0;2;1;0;0;0;4.99820882;7.80063264;0.151637352 +43;0;2;1;1;1;1;7.87533602;0;4.99841399;0.139394373;7.56720078;4.99234859;0;2;1;0;0;0;4.99841247;7.87533602;0.139394373 +44;0;2;1;1;1;1;7.94900879;0;4.99857192;0.128286852;7.61043204;4.99301916;0;2;1;0;0;0;4.99857055;7.94900879;0.128286852 +45;0;2;1;1;1;1;8.02171133;0;4.99869607;0.118202748;7.65569892;4.99365759;0;2;1;0;0;0;4.99869484;8.02171133;0.118202748 +46;0;2;1;1;1;1;8.09349392;0;4.99879501;0.109040492;7.70275844;4.99425954;0;2;1;0;0;0;4.99879389;8.09349392;0.109040492 +47;0;2;1;1;1;1;8.16439953;0;4.99887498;0.100708049;7.75140842;4.99482185;0;2;1;0;0;0;4.99887397;8.16439953;0.100708049 +48;0;2;1;1;1;1;8.23446561;0;4.99894053;0.0995205537;7.80147716;4.99534228;0;2;1;0;0;0;4.9989396;8.23446561;0.0995205537 +49;0;2;1;1;1;1;8.30372533;0;4.99899499;0.0986772372;7.85281636;4.9958194;0;2;1;0;0;0;4.99899414;8.30372533;0.0986772372 +50;0;2;1;1;1;1;8.37220842;0;4.99904081;0.0975593292;7.90529624;4.99625243;0;2;1;0;0;0;4.99904003;8.37220842;0.0975593292 +51;0;2;1;1;1;1;8.43994182;0;4.99907982;0.0962204058;7.95880187;4.99664128;0;2;1;0;0;0;4.9990791;8.43994182;0.0962204058 +52;0;2;1;0;0;0;8.49911522;0;4.99911338;0.0947080213;8.01321348;4.99698646;0;2;0;0;0;0;4.99911271;8.49911522;0.0947080213 +53;0;2;1;0;0;0;8.48787741;0;4.99914058;0.0930635307;8.06588908;4.99728901;0;2;0;0;0;0;4.99913996;8.48787741;0.0930635307 +54;0;2;1;0;0;0;8.47731383;0;4.99916196;0.0913222815;8.11494529;4.99755021;0;2;0;0;0;0;4.99916139;8.47731383;0.0913222815 +55;0;2;1;0;0;0;8.46743214;0;4.99917919;0.0895140735;8.16033782;4.99777228;0;2;0;0;0;0;4.99917865;8.46743214;0.0895140735 +56;0;2;1;0;0;0;8.45823975;0;4.99919345;0.0876637873;8.20202463;4.99795859;0;2;0;0;0;0;4.99919295;8.45823975;0.0876637873 +57;0;2;1;0;0;0;8.45003739;0;4.99920562;0.0857920568;8.2383623;4.99811359;0;2;0;0;0;0;4.99920515;8.45003739;0.0857920568 +58;0;2;1;0;0;0;8.4430193;0;4.99921637;0.083915915;8.26833166;4.99824261;0;2;0;0;0;0;4.99921593;8.4430193;0.083915915 +59;0;2;1;0;0;0;8.43698454;0;4.99922615;0.0820493558;8.29301435;4.99835122;0;2;0;0;0;0;4.99922575;8.43698454;0.0820493558 +60;0;2;1;0;0;0;8.43176611;0;4.99923527;0.080203778;8.3133087;4.9984445;0;2;0;0;0;0;4.9992349;8.43176611;0.080203778 +61;0;2;1;0;0;0;8.42722552;0;4.99924387;0.078388317;8.32995978;4.99852637;0;2;0;0;0;0;4.99924353;8.42722552;0.078388317 +62;0;2;1;0;0;0;8.42324786;0;4.99925203;0.0766101121;8.34358574;4.99859947;0;2;0;0;0;0;4.99925172;8.42324786;0.0766101121 +63;0;2;1;0;0;0;8.41973778;0;4.99925973;0.0748745574;8.35469956;4.99866544;0;2;0;0;0;0;4.99925945;8.41973778;0.0748745574 +64;0;2;1;0;0;0;8.41661612;0;4.99926698;0.073185555;8.36372703;4.99872529;0;2;0;0;0;0;4.99926671;8.41661612;0.073185555 +65;0;2;1;0;0;0;8.41381715;0;4.99927375;0.0715457627;8.37102165;4.99877973;0;2;0;0;0;0;4.99927351;8.41381715;0.0715457627 +66;0;2;1;0;0;0;8.41128629;0;4.99928006;0.0699568222;8.3768769;4.99882927;0;2;0;0;0;0;4.99927984;8.41128629;0.0699568222 +67;0;2;1;0;0;0;8.4089782;0;4.99928592;0.0684195582;8.38153651;4.99887439;0;2;0;0;0;0;4.99928572;8.4089782;0.0684195582 +68;0;2;1;0;0;0;8.40685525;0;4.99929135;0.0669341476;8.38520289;4.99891548;0;2;0;0;0;0;4.99929116;8.40685525;0.0669341476 +69;0;2;1;0;0;0;8.40488614;0;4.99929637;0.0655002598;8.38804413;4.99895292;0;2;0;0;0;0;4.9992962;8.40488614;0.0655002598 +70;0;2;1;0;0;0;8.40304489;0;4.99930101;0.0641171717;8.39019989;4.99898703;0;2;0;0;0;0;4.99930085;8.40304489;0.0641171717 +71;0;2;1;0;0;0;8.40130991;0;4.99930529;0.0627838619;8.39178614;4.99901813;0;2;0;0;0;0;4.99930515;8.40130991;0.0627838619 +72;0;2;1;0;0;0;8.39966326;0;4.99930924;0.0614990871;8.39289925;4.99904649;0;2;0;0;0;0;4.99930911;8.39966326;0.0614990871 +73;0;2;1;0;0;0;8.39809005;0;4.99931288;0.0602614435;8.39361925;4.99907235;0;2;0;0;0;0;4.99931276;8.39809005;0.0602614435 +74;0;2;1;0;0;0;8.39657787;0;4.99931624;0.0590694166;8.39401266;4.99909594;0;2;0;0;0;0;4.99931613;8.39657787;0.0590694166 +75;0;2;1;0;0;0;8.39511644;0;4.99931934;0.0579214207;8.39413471;4.99911747;0;2;0;0;0;0;4.99931924;8.39511644;0.0579214207 +76;0;2;1;0;0;0;8.39369535;0;4.9993222;0.0568158307;8.3940417;4.99913712;0;2;0;0;0;0;4.99932211;8.39369535;0.0568158307 +77;0;2;1;0;0;0;8.39228342;0;4.99932484;0.0557510066;8.3939018;4.99915506;0;2;0;0;0;0;4.99932475;8.39228342;0.0557510066 +78;0;2;1;0;0;0;8.39087176;0;4.99932727;0.0547253134;8.3937619;4.99917144;0;2;0;0;0;0;4.99932719;8.39087176;0.0547253134 +79;0;2;1;0;0;0;8.38946033;0;4.99932951;0.0537371356;8.39362201;4.99918639;0;2;0;0;0;0;4.99932944;8.38946033;0.0537371356 +80;0;2;1;0;0;0;8.38804912;0;4.99933157;0.052784889;8.39348212;4.99920005;0;2;0;0;0;0;4.99933151;8.38804912;0.052784889 +81;0;2;1;0;0;0;8.38663812;0;4.99933348;0.051867029;8.39334223;4.99921253;0;2;0;0;0;0;4.99933342;8.38663812;0.051867029 +82;0;2;1;0;0;0;8.3852273;0;4.99933523;0.0509820574;8.39320234;4.99922393;0;2;0;0;0;0;4.99933518;8.3852273;0.0509820574 +83;0;2;1;0;0;0;8.38381665;0;4.99933685;0.050128526;8.39306245;4.99923435;0;2;0;0;0;0;4.9993368;8.38381665;0.050128526 +84;0;2;1;0;0;0;8.38240616;0;4.99933835;0.0164053059;8.39292257;4.99924387;0;2;0;0;0;0;4.9993383;8.38240616;0.0164053059 +85;0;2;1;0;0;0;8.38099581;0;4.99933973;0.0109383371;8.39278269;4.99925257;0;2;0;0;0;0;4.99933969;8.38099581;0.0109383371 +86;0;2;1;0;0;0;8.37958561;0;4.99934101;0.00866272034;8.39264281;4.99926053;0;2;0;0;0;0;4.99934096;8.37958561;0.00866272034 +87;0;2;1;0;0;0;8.37817553;0;4.99934218;0.00734651981;8.39250293;4.9992678;0;2;0;0;0;0;4.99934215;8.37817553;0.00734651981 +88;0;2;1;0;0;0;8.37676556;0;4.99934327;0.00646092197;8.39236306;4.99927446;0;2;0;0;0;0;4.99934324;8.37676556;0.00646092197 +89;0;2;1;0;0;0;8.3753557;0;4.99934428;0.00581033545;8.39222319;4.99928055;0;2;0;0;0;0;4.99934425;8.3753557;0.00581033545 +90;0;2;1;0;0;0;8.37394595;0;4.99934521;0.00530428097;8.39208332;4.99928611;0;2;0;0;0;0;4.99934518;8.37394595;0.00530428097 +91;3;5;1;0;0;0;8.3726029;0;4.81079493;0.00510579372;8.39194345;4.99680111;0;5;0;0;0;0;4.81082797;8.3726029;0.00510579372 +92;3;5;1;0;0;0;8.37126227;0;4.5756801;0.00510579372;8.39180359;4.9906771;0;5;0;0;0;0;4.57572666;8.37126227;0.00510579372 +93;3;5;1;0;0;0;8.36466557;0;4.35129894;0.58231116;8.11248238;4.98471105;18745.6131;5;0;0;0;18.7456131;4.35134899;8.36466557;0.58231116 +94;3;5;1;0;0;0;8.35586694;0;4.13710381;0.947435197;8.08150919;4.98471105;34252.8577;5;0;0;0;34.2528577;4.13715156;8.35586694;0.947435197 +95;3;5;1;0;0;0;8.34674529;0;3.93202801;1.33790841;8.0473449;4.98471105;50406.6744;5;0;0;0;50.4066744;3.93207397;8.34674529;1.33790841 +96;3;5;1;0;0;0;8.33731697;0;3.89093861;1.71505814;8.01742435;4.98471105;65636.5443;5;0;0;0;65.6365443;3.89099018;8.33731697;1.71505814 +97;3;5;1;0;0;0;8.32770274;0;3.86377107;2.04390544;7.99790005;4.98471105;78651.3591;5;0;0;0;78.6513591;3.86381586;8.32770274;2.04390544 +98;3;5;1;0;0;0;8.31802721;0;3.84552644;2.32713951;7.98757796;4.98471105;89684.3671;5;0;0;0;89.6843671;3.84556436;8.31802721;2.32713951 +99;3;5;1;0;0;0;8.30840112;0;3.83319956;2.5640999;7.98598998;4.98471105;98800.9951;5;0;0;0;98.8009951;3.83323071;8.30840112;2.5640999 +100;3;5;1;0;0;0;8.2989342;0;3.82458625;2.74185818;7.99505907;4.98471105;105577.17;5;0;0;0;105.57717;3.82460512;8.2989342;2.74185818 +101;3;5;1;0;0;0;8.28982781;0;3.81793871;2.82346764;8.02088513;4.98471105;108671.144;5;0;0;0;108.671144;3.81794869;8.28982781;2.82346764 +102;3;5;1;0;0;0;8.28130581;0;3.81338342;2.88491407;8.04747315;4.98471105;110993.906;5;0;0;0;110.993906;3.8133917;8.28130581;2.88491407 +103;3;5;1;0;0;0;8.27369879;0;3.81028622;2.93655733;8.07103376;4.98471105;112941.686;5;0;0;0;112.941686;3.81029303;8.27369879;2.93655733 +104;3;5;1;0;0;0;8.26693897;0;3.80813208;2.9785179;8.09191636;4.98471105;114521.363;5;0;0;0;114.521363;3.80813767;8.26693897;2.9785179 +105;3;5;1;0;0;0;8.26092565;0;3.80659693;3.01338858;8.11015945;4.98471105;115832.173;5;0;0;0;115.832173;3.8066015;8.26092565;3.01338858 +106;3;5;1;0;0;0;8.25556854;0;3.80547507;3.04148964;8.1261866;4.98471105;116887.235;5;0;0;0;116.887235;3.8054788;8.25556854;3.04148964 +107;3;5;1;0;0;0;8.25078659;0;3.80463452;3.06459598;8.14008878;4.98471105;117753.925;5;0;0;0;117.753925;3.80463756;8.25078659;3.06459598 +108;3;5;1;0;0;0;8.24650727;0;3.80398953;3.08435684;8.1519357;4.98471105;118494.531;5;0;0;0;118.494531;3.803992;8.24650727;3.08435684 +109;3;5;1;0;0;0;8.24266571;0;3.8034835;3.09917092;8.1623887;4.98471105;119049.38;5;0;0;0;119.04938;3.80348551;8.24266571;3.09917092 +110;3;5;1;0;0;0;8.23920471;0;3.80307846;3.11227665;8.17112485;4.98471105;119539.989;5;0;0;0;119.539989;3.80308009;8.23920471;3.11227665 +111;3;5;1;0;0;0;8.23607329;0;3.80274842;3.12210603;8.17872467;4.98471105;119907.792;5;0;0;0;119.907792;3.80274975;8.23607329;3.12210603 +112;3;5;1;0;0;0;8.23322689;0;3.80247526;3.13025239;8.18511734;4.98471105;120212.517;5;0;0;0;120.212517;3.80247634;8.23322689;3.13025239 +113;3;5;1;0;0;0;8.23062648;0;3.80224606;3.13676943;8.19050277;4.98471105;120456.23;5;0;0;0;120.45623;3.80224694;8.23062648;3.13676943 +114;3;5;1;0;0;0;8.22823806;0;3.80205142;3.14166468;8.19506248;4.98471105;120639.255;5;0;0;0;120.639255;3.80205214;8.22823806;3.14166468 +115;3;5;1;0;0;0;8.22603127;0;3.80188436;3.14654338;8.19864788;4.98471105;120821.628;5;0;0;0;120.821628;3.80188495;8.22603127;3.14654338 +116;3;5;1;0;0;0;8.22397935;0;3.80174268;3.14980148;8.20170545;4.98471105;120943.403;5;0;0;0;120.943403;3.80174316;8.22397935;3.14980148 +117;3;5;1;0;0;0;8.22205132;0;3.80163197;3.15305664;8.20404499;4.98471105;121065.054;5;0;0;0;121.065054;3.80163236;8.22205132;3.15305664 +118;3;5;1;0;0;0;8.22023144;0;3.80153983;3.1563121;8.20576487;4.98471105;121186.701;5;0;0;0;121.186701;3.80154015;8.22023144;3.1563121 +119;3;5;1;0;0;0;8.21850465;0;3.80145931;3.15793649;8.20727529;4.98471105;121247.394;5;0;0;0;121.247394;3.80145957;8.21850465;3.15793649 +120;3;5;1;0;0;0;8.2168585;0;3.8013872;3.15956394;8.2083318;4.98471105;121308.198;5;0;0;0;121.308198;3.80138741;8.2168585;3.15956394 +121;0;2;1;0;0;0;8.17253429;0;3.96684612;3.14612013;8.20824618;4.98471105;121308.198;2;0;0;0;121.308198;3.9668296;8.17253429;3.14612013 +122;0;2;1;0;0;0;8.12689436;0;4.1343135;3.06559918;8.20810937;4.98471105;121308.198;2;0;0;0;121.308198;4.13429257;8.12689436;3.06559918 +123;0;2;1;0;0;0;8.08171119;0;4.29967471;2.96182116;8.20797257;4.98471105;121308.198;2;0;0;0;121.308198;4.29965086;8.08171119;2.96182116 +124;0;2;1;0;0;0;8.03705971;0;4.46273088;2.83923472;8.20783578;4.98471105;121308.198;2;0;0;0;121.308198;4.4627047;8.03705971;2.83923472 +125;0;2;1;0;0;0;7.99315681;0;4.62259498;2.70107021;8.20769898;4.98471105;121308.198;2;0;0;0;121.308198;4.6225671;7.99315681;2.70107021 +126;0;2;1;0;0;0;7.9588046;0;4.74537799;2.55062146;8.20756219;4.98471105;121308.198;2;0;0;0;121.308198;4.74535122;7.9588046;2.55062146 +127;0;2;1;0;0;0;7.93536;0;4.826731;2.39371367;8.20742539;4.98471105;121308.198;2;0;0;0;121.308198;4.82670528;7.93536;2.39371367 +128;0;2;1;0;0;0;7.91904583;0;4.88102572;2.23462456;8.2072886;4.98471105;114159.453;2;0;0;0;114.159453;4.88100096;7.91904583;2.23462456 +129;0;2;1;0;0;0;7.90744351;0;4.91746548;2.0760446;8.20715182;4.98471105;106051.405;2;0;0;0;106.051405;4.91744163;7.90744351;2.0760446 +130;0;2;1;0;0;0;7.89898136;0;4.9420324;1.91975963;8.20701503;4.98471105;97989.5353;2;0;0;0;97.9895353;4.94200944;7.89898136;1.91975963 +131;0;2;1;0;0;0;7.89262615;0;4.95866054;1.76700135;8.20687825;4.98471105;90036.0269;2;0;0;0;90.0360269;4.95863846;7.89262615;1.76700135 +132;0;2;1;0;0;0;7.8876927;0;4.96995967;1.61864501;8.20674147;4.98471105;82236.8942;2;0;0;0;82.2368942;4.96993847;7.8876927;1.61864501 +133;0;2;1;0;0;0;7.88372388;0;4.97767204;1.47532824;8.20660469;4.98471105;74627.6606;2;0;0;0;74.6276606;4.97765174;7.88372388;1.47532824 +134;0;2;1;0;0;0;7.88041332;0;4.98296594;1.3375259;8.20646792;4.98471105;67236.9068;2;0;0;0;67.2369068;4.98294654;7.88041332;1.3375259 +135;0;2;1;0;0;0;7.87755511;0;4.98662723;1.20559858;8.20633114;4.98471105;60088.5443;2;0;0;0;60.0885443;4.98660875;7.87755511;1.20559858 +136;0;2;1;0;0;0;7.8750106;0;4.98918567;1.07982477;8.20619437;4.98471105;53203.2909;2;0;0;0;53.2032909;4.98916813;7.8750106;1.07982477 +137;0;2;1;0;0;0;7.87268631;0;4.99099892;0.960424;8.2060576;4.98471105;46599.7054;2;0;0;0;46.5997054;4.99098232;7.87268631;0.960424 +138;0;2;1;0;0;0;7.87051916;0;4.99230864;0.847602139;8.20592084;4.98471105;40296.5165;2;0;0;0;40.2965165;4.99229301;7.87051916;0.847602139 +139;0;2;1;0;0;0;7.8684666;0;4.99327824;0.741518871;8.20578407;4.98471105;34310.6643;2;0;0;0;34.3106643;4.99326359;7.8684666;0.741518871 +140;0;2;1;0;0;0;7.86649995;0;4.99401822;0.642292588;8.20564731;4.98471105;28657.3535;2;0;0;0;28.6573535;4.99400455;7.86649995;0.642292588 +141;0;2;1;0;0;0;7.86459984;0;4.99460329;0.550018093;8.20551055;4.98471105;23350.8502;2;0;0;0;23.3508502;4.99459062;7.86459984;0.550018093 +142;0;2;1;0;0;0;7.86275189;0;4.9950388;0.463247182;8.20537379;4.98392803;18315.7406;2;0;0;0;18.3157406;4.99502629;7.86275189;0.463247182 +143;0;2;1;0;0;0;7.86094277;0;4.99540149;0.381985914;8.20523704;4.9827696;13559.1732;2;0;0;0;13.5591732;4.99539015;7.86094277;0.381985914 +144;0;2;1;0;0;0;7.85916923;0;4.99574695;0.308552333;8.20510029;4.98188624;9225.32703;2;0;0;0;9.22532703;4.99573683;7.85916923;0.308552333 +145;0;2;1;0;0;0;7.85743556;0;4.99629873;0.254252759;8.20496353;4.98212452;5998.40119;2;0;0;0;5.99840119;4.99629553;7.85743556;0.254252759 +146;0;2;1;0;0;0;7.85577253;0;4.99700757;0.230826052;8.20482679;4.98329164;4600.20731;2;0;0;0;4.60020731;4.99700489;7.85577253;0.230826052 +147;0;2;1;0;0;0;7.85417049;0;4.99751532;0.2111162;8.20469004;4.98445722;3421.01465;2;0;0;0;3.42101465;4.99751291;7.85417049;0.2111162 +148;0;2;1;0;0;0;7.85261222;0;4.99788285;0.193281618;8.2045533;4.98560443;2351.76277;2;0;0;0;2.35176277;4.9978807;7.85261222;0.193281618 +149;0;2;1;0;0;0;7.85108579;0;4.99815281;0.177134549;8.20441656;4.98672253;1381.82412;2;0;0;0;1.38182412;4.99815088;7.85108579;0.177134549 +150;0;2;1;0;0;0;7.84958287;0;4.99835438;0.162518313;8.20427982;4.98780377;502.306609;2;0;0;0;0.502306609;4.99835265;7.84958287;0.162518313 +151;0;2;1;0;0;0;7.8480976;0;4.99850756;0.149288117;8.20414308;4.98884241;0;2;0;0;0;0;4.99850601;7.8480976;0.149288117 +152;0;2;1;0;0;0;7.84662583;0;4.99862613;0.137310419;8.20400634;4.98983414;0;2;0;0;0;0;4.99862474;7.84662583;0.137310419 +153;0;2;1;0;0;0;7.84516457;0;4.99871961;0.126462525;8.20386961;4.99077567;0;2;0;0;0;0;4.99871836;7.84516457;0.126462525 +154;0;2;1;0;0;0;7.84371164;0;4.99879461;0.116632088;8.20373288;4.99166449;0;2;0;0;0;0;4.99879348;7.84371164;0.116632088 +155;0;2;1;0;0;0;7.84226541;0;4.99885574;0.107716551;8.20359615;4.99249863;0;2;0;0;0;0;4.99885472;7.84226541;0.107716551 +156;0;2;1;0;0;0;7.84082467;0;4.99890626;0.0999823696;8.20345943;4.99327664;0;2;0;0;0;0;4.99890534;7.84082467;0.0999823696 +157;0;2;1;0;0;0;7.83938848;0;4.99894851;0.0994328309;8.20332271;4.99399741;0;2;0;0;0;0;4.99894767;7.83938848;0.0994328309 +158;0;2;1;0;0;0;7.8379561;0;4.99898417;0.0985653576;8.20318599;4.99466022;0;2;0;0;0;0;4.9989834;7.8379561;0.0985653576 +159;0;2;1;0;0;0;7.83652692;0;4.99901447;0.0974372193;8.20304927;4.99526465;0;2;0;0;0;0;4.99901377;7.83652692;0.0974372193 +160;0;2;1;0;0;0;7.83510047;0;4.99904035;0.0961000782;8.20291255;4.99581063;0;2;0;0;0;0;4.9990397;7.83510047;0.0961000782 +161;0;2;1;0;0;0;7.83367635;0;4.99906252;0.0945993972;8.20277584;4.99629843;0;2;0;0;0;0;4.99906192;7.83367635;0.0945993972 +162;0;2;1;0;0;0;7.8322542;0;4.99908156;0.0929743829;8.20263912;4.99672874;0;2;0;0;0;0;4.999081;7.8322542;0.0929743829 +163;0;2;1;0;0;0;7.83083376;0;4.99909793;0.0912582874;8.20250241;4.99710274;0;2;0;0;0;0;4.99909741;7.83083376;0.0912582874 +164;0;2;1;0;0;0;7.82941477;0;4.99911204;0.0894789296;8.20236571;4.99742223;0;2;0;0;0;0;4.99911155;7.82941477;0.0894789296 +165;0;2;1;0;0;0;7.82799702;0;4.99912426;0.087659333;8.202229;4.99768985;0;2;0;0;0;0;4.99912379;7.82799702;0.087659333 +166;0;2;1;0;0;0;7.82658036;0;4.99913493;0.0858184105;8.2020923;4.99790927;0;2;0;0;0;0;4.99913448;7.82658036;0.0858184105 +167;0;2;1;0;0;0;7.82516464;0;4.9991444;0.0839716507;8.2019556;4.99808557;0;2;0;0;0;0;4.99914398;7.82516464;0.0839716507 +168;0;2;1;0;0;0;7.82374976;0;4.99915304;0.0821317685;8.2018189;4.99822539;0;2;0;0;0;0;4.99915264;7.82374976;0.0821317685 +169;0;2;1;0;0;0;7.82233567;0;4.9991612;0.0803092807;8.20168221;4.99833672;0;2;0;0;0;0;4.99916082;7.82233567;0.0803092807 +170;0;2;1;0;0;0;7.82092233;0;4.99916914;0.0785129378;8.20154551;4.99842777;0;2;0;0;0;0;4.99916879;7.82092233;0.0785129378 +171;0;2;1;0;0;0;7.81950974;0;4.99917701;0.0767499621;8.20140882;4.99850534;0;2;0;0;0;0;4.99917669;7.81950974;0.0767499621 diff --git a/tools/vehicle_graph/tests/brakes.script b/tools/vehicle_graph/tests/brakes.script new file mode 100644 index 00000000..5a72adff --- /dev/null +++ b/tools/vehicle_graph/tests/brakes.script @@ -0,0 +1,7 @@ +# events for: -graph-run brakes.vhg -script brakes.script +# options: -every 100 +# braked start: running position charges the pipe and releases the brake; then service braking and release +0.0 value brake 0 +90.0 value brake 3 +120.0 value brake 0 +170.0 value brake 0 diff --git a/tools/vehicle_graph/tests/brakes.vhg b/tools/vehicle_graph/tests/brakes.vhg new file mode 100644 index 00000000..ebe7440f --- /dev/null +++ b/tools/vehicle_graph/tests/brakes.vhg @@ -0,0 +1,36 @@ +// expect: ok +// brake pipe of a locomotive with 400 m of train behind it: the FV4a valve charges it from the braked state, +// then the LSt distributor brakes and releases with the pipe. the main reservoir is kept by the compressor +{ + "format": "maszyna-vehicle-graph", + "version": 2, + "vehicle": { "name": "brake test" }, + "body": { "mass": 80000, "length": 16, "wheel_diameter": 1.25, "axle_arrangement": "Bo'Bo'", "coupler_allowed_flag": 3 }, + "profiles": { "brake_pipe": 1 }, + "nodes": [ + { "id": "brake", "type": "cab.switch", "params": { "behaviour": "multi", "positions": 9, "min": -2, "max": 6, "default": 0 } }, + { "id": "on", "type": "logic.const", "params": { "value": 1 } }, + { "id": "comp", "type": "pn.compressor", "params": { "flow": 0.06, "max_pressure": 9 } }, + { "id": "gov", "type": "pn.governor", "params": { "min": 7.5, "max": 8.5 } }, + { "id": "comp_on", "type": "logic.and" }, + { "id": "res", "type": "pn.reservoir", "params": { "volume": 0.8 } }, + { "id": "fv4a", "type": "br.handle", "params": { "type": "FV4a" } }, + { "id": "pipe", "type": "br.pipe", "params": { "preview_train_length": 400 } }, + { "id": "dist", "type": "br.distributor", "params": { "type": "LSt", "mode": "P", "max_bp": 4.0, "cyl_count": 4, "cyl_radius": 0.1524, "cyl_dist": 0.1, "volume": 150, + "delay1": 15, "delay2": 4, "delay3": 25, "delay4": 13 } }, + { "id": "rig", "type": "br.rigging", "params": { "bcm": 4.0, "bcs": 1, "bsa": 0.5, "bre": 0.85, "nbpa": 2, "material": "P10-Bgu" } } + ], + "connections": [ + { "from": "on.q", "to": "comp_on.a" }, + { "from": "gov.need", "to": "comp_on.b" }, + { "from": "comp_on.q", "to": "comp.on" }, + { "from": "comp.air", "to": "res.in" }, + { "from": "res.p", "to": "gov.p" }, + { "from": "res.out", "to": "fv4a.zg" }, + { "from": "brake.pos", "to": "fv4a.pos" }, + { "from": "fv4a.pg", "to": "pipe.in" }, + { "from": "pipe.dist", "to": "dist.pg" }, + { "from": "res.pz", "to": "dist.feed" }, + { "from": "dist.bc", "to": "rig.bc" } + ] +} diff --git a/tools/vehicle_graph/tests/consist-short.vhg b/tools/vehicle_graph/tests/consist-short.vhg new file mode 100644 index 00000000..6aa403c6 --- /dev/null +++ b/tools/vehicle_graph/tests/consist-short.vhg @@ -0,0 +1,15 @@ +// expect: ok +// vehicle of the consist test (consist.vhg) with the direction and door lines only: the lines "nastawnik" and "suma" +// end at it +{ + "format": "maszyna-vehicle-graph", + "version": 2, + "vehicle": { "name": "train line test, short" }, + "body": { "mass": 40000, "length": 20 }, + "nodes": [ + { "id": "l_dir", "type": "if.line", "params": { "name": "kierunek", "combine": "maxabs", "reverse": true } }, + { "id": "l_door_l", "type": "if.line", "params": { "name": "drzwi_lewe", "pair": "drzwi_prawe", "combine": "or" } }, + { "id": "l_door_r", "type": "if.line", "params": { "name": "drzwi_prawe", "pair": "drzwi_lewe", "combine": "or" } } + ], + "connections": [] +} diff --git a/tools/vehicle_graph/tests/consist.expected.csv b/tools/vehicle_graph/tests/consist.expected.csv new file mode 100644 index 00000000..48702579 --- /dev/null +++ b/tools/vehicle_graph/tests/consist.expected.csv @@ -0,0 +1,41 @@ +time;dir.q;l_dir.out;l_dir.vehicles;l_dir.drivers;ctrl.q;ctrl_act.q;l_ctrl.out;l_ctrl.vehicles;l_ctrl.drivers;l_ctrl_min.out;l_ctrl_min.vehicles;l_ctrl_min.drivers;door_l.q;door_r.q;l_door_l.out;l_door_l.vehicles;l_door_l.drivers;l_door_r.out;l_door_r.vehicles;l_door_r.drivers;one.q;l_sum.out;l_sum.vehicles;l_sum.drivers;body:v;body:ft;body:fb;body:pg;body:zg;body:bc;2:dir.q;2:l_dir.out;2:l_dir.vehicles;2:l_dir.drivers;2:ctrl.q;2:ctrl_act.q;2:l_ctrl.out;2:l_ctrl.vehicles;2:l_ctrl.drivers;2:l_ctrl_min.out;2:l_ctrl_min.vehicles;2:l_ctrl_min.drivers;2:door_l.q;2:door_r.q;2:l_door_l.out;2:l_door_l.vehicles;2:l_door_l.drivers;2:l_door_r.out;2:l_door_r.vehicles;2:l_door_r.drivers;2:one.q;2:l_sum.out;2:l_sum.vehicles;2:l_sum.drivers;2:body:v;2:body:ft;2:body:fb;2:body:pg;2:body:zg;2:body:bc;3:l_dir.out;3:l_dir.vehicles;3:l_dir.drivers;3:l_door_l.out;3:l_door_l.vehicles;3:l_door_l.drivers;3:l_door_r.out;3:l_door_r.vehicles;3:l_door_r.drivers;3:body:v;3:body:ft;3:body:fb;3:body:pg;3:body:zg;3:body:bc;4:dir.q;4:l_dir.out;4:l_dir.vehicles;4:l_dir.drivers;4:ctrl.q;4:ctrl_act.q;4:l_ctrl.out;4:l_ctrl.vehicles;4:l_ctrl.drivers;4:l_ctrl_min.out;4:l_ctrl_min.vehicles;4:l_ctrl_min.drivers;4:door_l.q;4:door_r.q;4:l_door_l.out;4:l_door_l.vehicles;4:l_door_l.drivers;4:l_door_r.out;4:l_door_r.vehicles;4:l_door_r.drivers;4:one.q;4:l_sum.out;4:l_sum.vehicles;4:l_sum.drivers;4:body:v;4:body:ft;4:body:fb;4:body:pg;4:body:zg;4:body:bc +0.1;0;0;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;0;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;0;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +0.2;0;0;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;0;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;0;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +0.3;0;0;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;0;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;0;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +0.4;0;0;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;0;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;0;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +0.5;0;0;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;0;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;0;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +0.6;1;1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;-1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +0.7;1;1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;-1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +0.8;1;1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;-1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +0.9;1;1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;-1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +1;1;1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;0;-1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +1.1;0;-1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +1.2;0;-1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +1.3;0;-1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +1.4;0;-1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +1.5;0;-1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0;0;0;2;0;0;2;0;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +1.6;0;-1;4;3;0.6;1;0.6;2;1;0.6;2;1;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;0;0.6;2;1;0.6;2;1;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +1.7;0;-1;4;3;0.6;1;0.6;2;1;0.6;2;1;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;0;0.6;2;1;0.6;2;1;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +1.8;0;-1;4;3;0.6;1;0.6;2;1;0.6;2;1;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;0;0.6;2;1;0.6;2;1;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +1.9;0;-1;4;3;0.6;1;0.6;2;1;0.6;2;1;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;0;0.6;2;1;0.6;2;1;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +2;0;-1;4;3;0.6;1;0.6;2;1;0.6;2;1;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;0;0.6;2;1;0.6;2;1;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0;0;0;1;0;0;1;0;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +2.1;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +2.2;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +2.3;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +2.4;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +2.5;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;0;0;0;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +2.6;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;1;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;1;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +2.7;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;1;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;1;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +2.8;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;1;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;1;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +2.9;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;1;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;1;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +3;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;1;4;3;0;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;1;4;3;0;4;3;1;1;1;1;0;0;0;0;0;0 +3.1;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;1;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;1;4;3;1;1;1;1;0;0;0;0;0;0 +3.2;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;1;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;1;4;3;1;1;1;1;0;0;0;0;0;0 +3.3;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;1;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;1;4;3;1;1;1;1;0;0;0;0;0;0 +3.4;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;1;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;1;4;3;1;1;1;1;0;0;0;0;0;0 +3.5;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;1;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;1;4;3;1;1;1;1;0;0;0;0;0;0 +3.6;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;1;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;1;4;3;1;1;1;1;0;0;0;0;0;0 +3.7;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;1;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;1;4;3;1;1;1;1;0;0;0;0;0;0 +3.8;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;1;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;1;4;3;1;1;1;1;0;0;0;0;0;0 +3.9;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;1;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;1;4;3;1;1;1;1;0;0;0;0;0;0 +4;0;-1;4;3;0.6;1;0.9;2;2;0.6;2;2;0;0;0;4;3;1;4;3;1;2;2;2;0;0;0;0;0;0;1;1;4;3;0.9;1;0.9;2;2;0.6;2;2;1;0;1;4;3;0;4;3;1;2;2;2;0;0;0;0;0;0;-1;4;3;0;4;3;1;4;3;0;0;0;0;0;0;0;-1;4;3;0.3;1;0.3;1;1;0.3;1;1;0;0;0;4;3;1;4;3;1;1;1;1;0;0;0;0;0;0 diff --git a/tools/vehicle_graph/tests/consist.script b/tools/vehicle_graph/tests/consist.script new file mode 100644 index 00000000..7aecd3a9 --- /dev/null +++ b/tools/vehicle_graph/tests/consist.script @@ -0,0 +1,17 @@ +# events for: -graph-run consist.vhg -script consist.script -consist consist.vhg:r -consist consist-short.vhg -consist consist.vhg +# options: -consist consist.vhg:r -consist consist-short.vhg -consist consist.vhg +# direction from vehicle 1, then from vehicle 2 (facing the other way) +0.5 force dir.q 1 +1.0 force dir.q 0 +1.0 force 2:dir.q 1 +# controller: vehicle 1 drives, vehicle 2 gives a value without driving, then drives as well; vehicle 4 alone +1.5 force ctrl.q 0.6 +1.5 force ctrl_act.q 1 +1.5 force 2:ctrl.q 0.9 +2.0 force 2:ctrl_act.q 1 +2.0 force 4:ctrl.q 0.3 +2.0 force 4:ctrl_act.q 1 +# left doors of vehicle 1: the right doors of vehicle 2 +2.5 force door_l.q 1 +3.0 force door_l.q 0 +3.0 force 2:door_l.q 1 diff --git a/tools/vehicle_graph/tests/consist.vhg b/tools/vehicle_graph/tests/consist.vhg new file mode 100644 index 00000000..48bb4054 --- /dev/null +++ b/tools/vehicle_graph/tests/consist.vhg @@ -0,0 +1,31 @@ +// expect: ok +// train lines of the multiple unit control (if.line); run as a consist of four vehicles (see consist.script): +// 1 this, 2 this reversed, 3 consist-short.vhg (without the lines "nastawnik" and "suma"), 4 this +{ + "format": "maszyna-vehicle-graph", + "version": 2, + "vehicle": { "name": "train line test" }, + "body": { "mass": 40000, "length": 20 }, + "nodes": [ + { "id": "dir", "type": "logic.const", "params": { "value": 0 } }, + { "id": "l_dir", "type": "if.line", "params": { "name": "kierunek", "combine": "maxabs", "reverse": true } }, + { "id": "ctrl", "type": "logic.const", "params": { "value": 0 } }, + { "id": "ctrl_act", "type": "logic.const", "params": { "value": 0 } }, + { "id": "l_ctrl", "type": "if.line", "params": { "name": "nastawnik", "combine": "max" } }, + { "id": "l_ctrl_min", "type": "if.line", "params": { "name": "nastawnik", "combine": "min" } }, + { "id": "door_l", "type": "logic.const", "params": { "value": 0 } }, + { "id": "door_r", "type": "logic.const", "params": { "value": 0 } }, + { "id": "l_door_l", "type": "if.line", "params": { "name": "drzwi_lewe", "pair": "drzwi_prawe", "combine": "or" } }, + { "id": "l_door_r", "type": "if.line", "params": { "name": "drzwi_prawe", "pair": "drzwi_lewe", "combine": "or" } }, + { "id": "one", "type": "logic.const", "params": { "value": 1 } }, + { "id": "l_sum", "type": "if.line", "params": { "name": "suma", "combine": "sum", "coupling": "permanent" } } + ], + "connections": [ + { "from": "dir.q", "to": "l_dir.in" }, + { "from": "ctrl.q", "to": "l_ctrl.in" }, + { "from": "ctrl_act.q", "to": "l_ctrl.active" }, + { "from": "door_l.q", "to": "l_door_l.in" }, + { "from": "door_r.q", "to": "l_door_r.in" }, + { "from": "one.q", "to": "l_sum.in" } + ] +} diff --git a/tools/vehicle_graph/tests/diesel-electric.expected.csv b/tools/vehicle_graph/tests/diesel-electric.expected.csv new file mode 100644 index 00000000..6d0e065e --- /dev/null +++ b/tools/vehicle_graph/tests/diesel-electric.expected.csv @@ -0,0 +1,243 @@ +time;starter.pos;starter.index;throttle.pos;throttle.index;reverser.pos;reverser.index;power.q;engine.running;engine.starting;engine.rpm;engine.torque;engine.fill;engine.power;engine.fuel_rate;gen.u;gen.i;gen.p;motors.i;motors.u_motor;motors.overload;axles.force;axles.slip;vs.v;vs.v_signed;vs.accel;vs.wheel_rpm;vs.slipping;vs.cab;vs.time;starter:position;throttle:position;reverser:position;engine:fuel_used;motors:overload;body:v;body:ft;body:fb;body:pg;body:zg;body:bc +0.5;0;0;0;0;1;2;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0.5;0;0;2;0;0;0;0;0;0;0;0 +1;0;0;0;0;1;2;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;0;2;0;0;0;0;0;0;0;0 +1.5;1;1;0;0;1;2;0;0;1;66.996178;137.253729;0;-2.10474701;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1.5;1;0;2;0;0;0;0;0;0;0;0 +2;0;0;0;0;1;2;0;0;1;94.1846786;93.8950949;0;-2.95889894;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;2;0;0;2;0;0;0;0;0;0;0;0 +2.5;0;0;0;0;1;2;0;0;1;113.356265;68.1530375;0;-3.56119209;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;2.5;0;0;2;0;0;0;0;0;0;0;0 +3;0;0;0;0;1;2;0;1;1;127.515586;114.770227;0.1;-3.1571644;0.794920102;0;0;0;0;0;0;0;0;0;0;0;0;0;1;3;0;0;2;2.2081114e-06;0;0;0;0;0;0;0 +3.5;0;0;0;0;1;2;0;1;1;326.694918;1411.16336;0.995361602;40.9858647;20.2713873;0;0;0;0;0;0;0;0;0;0;0;0;0;1;3.5;0;0;2;0.00157660663;0;0;0;0;0;0;0 +4;0;0;0;0;1;2;0;1;0;631.97705;-49.0335174;0.102335624;-3.24506181;4.0317014;0;0;0;0;0;0;0;0;0;0;0;0;0;1;4;0;0;2;0.00455293873;0;0;0;0;0;0;0 +4.5;0;0;0;0;1;2;0;1;0;586.135122;28.9243247;0.140574528;1.77537292;5.13646803;0;0;0;0;0;0;0;0;0;0;0;0;0;1;4.5;0;0;2;0.00479294229;0;0;0;0;0;0;0 +5;0;0;0;0;1;2;0;1;0;590.205593;-2.79783624;0.126454253;-0.172923585;4.65261345;0;0;0;0;0;0;0;0;0;0;0;0;0;1;5;0;0;2;0.00548082255;0;0;0;0;0;0;0 +5.5;0;0;0;0;1;2;0;1;0;590.4254;-0.505747785;0.127399132;-0.0312699826;4.68912394;0;0;0;0;0;0;0;0;0;0;0;0;0;1;5.5;0;0;2;0.00613527512;0;0;0;0;0;0;0 +6;0;0;0;0;1;2;0;1;0;590.429772;-0.0467047499;0.127593796;-0.00288773886;4.69632361;0;0;0;0;0;0;0;0;0;0;0;0;0;1;6;0;0;2;0.00678770191;0;0;0;0;0;0;0 +6.5;0;0;0;0;1;2;0;1;0;590.429086;-0.00293623304;0.127612509;-0.000181546083;4.69700692;0;0;0;0;0;0;0;0;0;0;0;0;0;1;6.5;0;0;2;0.0074400659;0;0;0;0;0;0;0 +7;0;0;0;0;1;2;0;1;0;590.428977;-0.000101560948;0.12761373;-6.27947053e-06;4.69705099;0;0;0;0;0;0;0;0;0;0;0;0;0;1;7;0;0;2;0.00809243344;0;0;0;0;0;0;0 +7.5;0;0;0;0;1;2;0;1;0;590.428968;3.84304502e-06;0.127613776;2.37613848e-07;4.69705261;0;0;0;0;0;0;0;0;0;0;0;0;0;1;7.5;0;0;2;0.00874480177;0;0;0;0;0;0;0 +8;0;0;0;0;1;2;0;1;0;590.428967;1.03901431e-06;0.127613775;6.4241815e-08;4.69705257;0;0;0;0;0;0;0;0;0;0;0;0;0;1;8;0;0;2;0.00939717017;0;0;0;0;0;0;0 +8.5;0;0;0;0;1;2;0;1;0;590.428967;1.06578852e-07;0.127613774;6.58972534e-09;4.69705255;0;0;0;0;0;0;0;0;0;0;0;0;0;1;8.5;0;0;2;0.0100495386;0;0;0;0;0;0;0 +9;0;0;0;0;1;2;0;1;0;590.428967;7.34246441e-09;0.127613774;4.53981468e-10;4.69705255;0;0;0;0;0;0;0;0;0;0;0;0;0;1;9;0;0;2;0.010701907;0;0;0;0;0;0;0 +9.5;0;0;0;0;1;2;0;1;0;590.428967;3.11047188e-10;0.127613774;1.9231916e-11;4.69705255;0;0;0;0;0;0;0;0;0;0;0;0;0;1;9.5;0;0;2;0.0113542754;0;0;0;0;0;0;0 +10;0;0;0;0;1;2;0;1;0;590.428967;-3.24007488e-12;0.127613774;-2.00332458e-13;4.69705255;0;0;0;0;0;0;0;0;0;0;0;0;0;1;10;0;0;2;0.0120066438;0;0;0;0;0;0;0 +10.5;0;0;0.1;1;1;2;1;1;0;670.454268;39.4380792;0.133704093;2.76893992;5.58822742;20.1705812;1074.7171;21.6776685;270.701692;14.7565473;0;21745.5506;0;0.232006833;0.0644463425;0.271554057;1.23083448;0;1;10.5;0;1;2;0.0137556847;0;0.241895459;21.7455506;0;0;0;0 +11;0;0;0.1;1;1;2;1;1;0;667.015838;450.899608;0.296644456;31.4952182;12.3348136;25.7754468;1209.09898;31.1650664;302.165188;19.732143;0;25396.6528;0;0.792453112;0.220125864;0.324270651;4.20409433;0;1;11;0;1;2;0.0153942814;0;0.804120579;25.3966528;0;0;0;0 +11.5;0;0;0.1;1;1;2;1;1;0;666.5914;491.052319;0.312620406;34.2780487;12.9908401;27.8558127;1145.08491;31.8972707;285.849398;22.1388248;0;23491.3038;0;1.35631895;0.376755264;0.298968186;7.19549552;0;1;11.5;0;1;2;0.0171945799;0;1.36705796;23.4913038;0;0;0;0 +12;0;0;0.1;1;1;2;1;1;0;667.133016;475.937399;0.306504974;33.2499414;12.7470641;29.0113247;1059.73243;30.7442417;264.519509;23.7209345;0;21039.9975;0;1.86375245;0.517709013;0.26576193;9.88751382;0;1;12;0;1;2;0.0189861046;0;1.87329694;21.0399975;0;0;0;0 +12.5;0;0;0.1;1;1;2;1;1;0;667.832511;452.999404;0.29726655;31.6806297;12.375815;29.8036193;982.162082;29.2719848;245.181032;24.8999986;0;18861.2865;0;2.3142481;0.642846695;0.236175042;12.2774675;0;1;12.5;0;1;2;0.0207306208;0;2.32273087;18.8612865;0;0;0;0 +13;0;0;0.1;1;1;2;1;1;0;668.51369;430.161257;0.288083317;30.1141227;12.0057314;30.3929225;915.592056;27.8275184;228.591711;25.8210883;0;17029.8732;0;2.71584876;0.754402433;0.211293096;14.4080252;0;1;13;0;1;2;0.0224230262;0;2.723439;17.0298732;0;0;0;0 +13.5;0;0;0.1;1;1;2;1;1;0;669.138719;409.047711;0.279604369;28.6628087;11.6632698;30.847181;858.894322;26.4944686;214.462397;26.557933;0;15500.7033;0;3.07654651;0.854596254;0.190516796;16.3215862;0;1;13.5;0;1;2;0.0240658614;0;3.08339147;15.5007033;0;0;0;0 +14;0;0;0.1;1;1;2;1;1;0;669.701582;389.968823;0.271950888;27.348898;11.3535589;31.2050548;810.419282;25.2891781;202.380762;27.1574395;0;14217.9005;0;3.40302865;0.945285737;0.173089076;18.0536277;0;1;14;0;1;2;0.0256634415;0;3.40924837;14.2179005;0;0;0;0 +14.5;0;0;0.1;1;1;2;1;1;0;670.205221;372.86774;0.26509735;26.1692475;11.0757566;31.4919001;768.688196;24.2074519;191.978398;27.6523321;0;13133.3894;0;3.7007032;1.02797311;0.158356744;19.6328402;0;1;14.5;0;1;2;0.0272202918;0;3.70639429;13.1333894;0;0;0;0 +15;0;0;0.1;1;1;2;1;1;0;670.655313;357.571409;0.258972245;25.1125476;10.8271163;31.7253584;732.486953;23.2384111;182.953071;28.066297;0;12208.6621;0;3.97392278;1.10386744;0.145796238;21.0823151;0;1;15;0;1;2;0.0287406604;0;3.97916309;12.2086621;0;0;0;0 +15.5;0;0;0.3;3;1;2;1;1;0;834.570505;1166.58228;0.519352288;101.954647;27.0199966;66.2089058;1888.67732;125.047259;472.968724;56.7495313;0;46430.5888;0;4.75755114;1.32154198;0.607325998;25.2395927;0;1;15.5;0;3;2;0.0337817157;0;4.77946445;46.4305888;0;0;0;0 +16;0;0;0.3;3;1;2;1;1;0;818.209046;1645.10742;0.694313076;140.957165;35.4143953;72.7636377;1848.45069;134.499996;461.741084;63.528816;0;45003.176;0;5.84337774;1.62316048;0.589988563;31.0000817;0;1;16;0;3;2;0.0385463518;0;5.86459432;45.003176;0;0;0;0 +16.5;0;0;0.3;3;1;2;1;1;0;815.539831;1727.51908;0.724707691;147.535539;36.8441258;77.9189028;1768.46472;137.796831;441.724903;69.0844048;0;42470.6689;0;6.87366991;1.90935275;0.555703579;36.4659515;0;1;16.5;0;3;2;0.0436083486;0;6.89365117;42.4706689;0;0;0;0 +17;0;0;0.3;3;1;2;1;1;0;814.784509;1756.26477;0.735265139;149.851596;37.3462455;82.4964501;1691.08003;139.5081;422.391971;74.0486107;0;40040.6901;0;7.84343615;2.17873226;0.522746124;41.6107211;0;1;17;0;3;2;0.048768903;0;7.86223191;40.0406901;0;0;0;0 +17.5;0;0;0.3;3;1;2;1;1;0;814.629111;1764.45614;0.738252668;150.521803;37.490839;86.5494279;1617.22323;139.969746;403.946479;78.4704983;0;37738.3951;0;8.75542522;2.43206256;0.491505014;46.4489734;0;1;17.5;0;3;2;0.0539695992;0;8.77309759;37.7383951;0;0;0;0 +18;0;0;0.3;3;1;2;1;1;0;814.839494;1758.94831;0.736204061;150.090694;37.3964595;90.1304055;1547.37493;139.46553;386.505258;82.4003003;0;35577.3118;0;9.61290934;2.67025259;0.462169919;50.9980679;0;1;18;0;3;2;0.0591723543;0;9.62952706;35.5773118;0;0;0;0 +18.5;0;0;0.3;3;1;2;1;1;0;815.311589;1743.40866;0.730471989;148.850889;37.1267891;93.2912837;1481.80046;138.239067;370.133343;85.8886168;0;33564.0381;0;10.4194177;2.89428269;0.434833069;55.2767275;0;1;18.5;0;3;2;0.0643489307;0;10.4350527;33.5640381;0;0;0;0 +19;0;0;0.3;3;1;2;1;1;0;815.969816;1720.64596;0.722093267;147.02603;36.7305641;96.0810503;1420.57858;136.490682;354.849578;88.9840587;0;31699.1454;0;11.1785666;3.10515739;0.409504161;59.3041377;0;1;19;0;3;2;0.0694785844;0;11.1932912;31.6991454;0;0;0;0 +19.5;0;0;0.3;3;1;2;1;1;0;816.754889;1692.89199;0.711891927;144.793681;36.2464952;98.5448155;1363.64952;134.380591;340.638447;91.7320466;0;29978.8264;0;11.8939318;3.30386993;0.386133053;63.0992677;0;1;19.5;0;3;2;0.0745465882;0;11.9078164;29.9788264;0;0;0;0 +20;0;0;0.3;3;1;2;1;1;0;817.620778;1661.89096;0.70051149;142.292845;35.7048647;100.723385;1310.85814;132.034069;327.460775;94.1741697;0;28396.3571;0;12.5689592;3.49137755;0.364629673;66.6803995;0;1;20;0;3;2;0.0795429762;0;12.582071;28.3963571;0;0;0;0 +20.5;0;0;0.3;3;1;2;1;1;0;818.532489;1628.97718;0.688443404;139.630258;35.1288857;102.653168;1261.9884;129.547107;315.262329;96.3479214;0;26943.2508;0;13.2069085;3.66858571;0.344879746;70.0648259;0;1;20.5;0;3;2;0.0844614723;0;13.2193106;26.9432508;0;0;0;0 +21;0;0;0.3;3;1;2;1;1;0;819.463984;1595.15003;0.676054866;136.886313;34.535999;104.366305;1216.78975;126.991849;303.980351;98.2866975;0;25610.1219;0;13.8108209;3.83633913;0.326756576;73.2686804;0;1;21;0;3;2;0.0892986008;0;13.8225716;25.6101219;0;0;0;0 +21.5;0;0;0.3;3;1;2;1;1;0;820.396319;1561.1422;0.663614214;134.120383;33.939043;105.890936;1174.99631;124.421459;293.548331;100.01997;0;24387.3028;0;14.3835045;3.99541791;0.310129465;76.3068613;0;1;21.5;0;3;2;0.0940529699;0;14.3946577;24.3873028;0;0;0;0 +22;0;0;0.3;3;1;2;1;1;0;821.316053;1527.47835;0.651312818;131.375382;33.3472592;107.251551;1136.34035;121.874264;283.899355;101.573564;0;23265.2645;0;14.9275333;4.14653703;0.294869462;79.1930238;0;1;22;0;3;2;0.0987247054;0;14.9381381;23.2652645;0;0;0;0 +22.5;0;0;0.3;3;1;2;1;1;0;822.213938;1494.52366;0.639283177;128.681539;32.7671236;108.469361;1100.56128;119.377179;274.968328;102.969995;0;22234.8868;0;15.4452537;4.29034826;0.280853059;81.9396159;0;1;22.5;0;3;2;0.103315012;0;15.4553548;22.2348868;0;0;0;0 +23;0;0;0.3;3;1;2;1;1;0;823.08388;1462.52287;0.627613451;126.059442;32.203016;109.562687;1067.41133;116.948453;266.693381;104.228819;0;21287.6175;0;15.9387966;4.42744351;0.267964389;84.5579423;0;1;23;0;3;2;0.10782584;0;15.9484344;21.2876175;0;0;0;0 +23.5;0;0;0.3;3;1;2;1;1;0;823.922116;1431.63101;0.616358884;123.522444;31.6577489;110.547315;1036.65875;114.599842;259.016664;105.366982;0;20415.5547;0;16.4100922;4.55835895;0.25609635;87.0582431;0;1;23.5;0;3;2;0.112259628;0;16.4193035;20.4155547;0;0;0;0 +24;0;0;0.3;3;1;2;1;1;0;824.726587;1401.93717;0.605550615;121.078537;31.1329773;111.436841;1008.08941;112.338299;251.884729;106.399146;0;19611.4741;0;16.8608864;4.68357955;0.245150995;89.4497804;0;1;24;0;3;2;0.116619123;0;16.8697042;19.6114741;0;0;0;0 +24.5;0;0;0.3;3;1;2;1;1;0;825.49646;1373.4827;0.595202407;118.731793;30.6295138;112.242966;981.507091;110.167267;245.248614;107.337994;0;18868.8214;0;17.292757;4.80354361;0.235039446;91.7409251;0;1;24.5;0;3;2;0.120907237;0;17.3012114;18.8688214;0;0;0;0 +25;0;0;0.3;3;1;2;1;1;0;826.231763;1346.27497;0.585315703;116.483464;30.1475672;112.97577;956.733164;108.087666;239.063748;108.194495;0;18181.6835;0;17.70713;4.91864723;0.225681501;93.9392424;0;1;25;0;3;2;0.12512695;0;17.7152481;18.1816835;0;0;0;0 +25.5;0;0;0.6;6;1;2;1;1;0;933.635467;2624.54342;0.99786281;256.601813;58.0775634;139.326072;1368.00035;190.598115;342.687187;132.472328;0;30225.9801;0;18.3087611;5.08576697;0.387322667;97.1309944;0;1;25.5;0;6;2;0.132159536;0;18.3227451;30.2259801;0;0;0;0 +26;0;0;0.6;6;1;2;1;1;0;1065.5366;2694.85339;0.999273517;300.699104;66.3762877;149.21654;1477.42929;220.456887;369.850926;141.819522;0;33529.4469;0;19.0491679;5.29143552;0.432252466;101.058974;0;1;26;0;6;2;0.140814099;0;19.0647584;33.5294469;0;0;0;0 +26.5;0;0;0.6;6;1;2;1;1;0;1076.60772;2262.66443;0.854611177;255.097532;57.3569789;154.953858;1492.3265;231.241748;372.945158;147.494954;0;33908.6983;0;19.8431284;5.51198012;0.438336497;105.271066;0;1;26.5;0;6;2;0.148648377;0;19.8589003;33.9086983;0;0;0;0 +27;0;0;0.6;6;1;2;1;1;0;1077.17229;2216.31206;0.839135084;250.002702;56.3478388;158.572116;1462.9642;231.98533;365.592266;151.260271;0;33008.4221;0;20.6212801;5.72813337;0.426076396;109.399289;0;1;27;0;6;2;0.15645763;0;20.6366099;33.0084221;0;0;0;0 +27.5;0;0;0.6;6;1;2;1;1;0;1077.11667;2214.39135;0.838496347;249.773144;56.3020399;161.975382;1433.99525;232.271928;358.359064;154.808201;0;32126.0583;0;21.3770522;5.93807007;0.414022587;113.408784;0;1;27.5;0;6;2;0.164272143;0;21.3919487;32.1260583;0;0;0;0 +28;0;0;0.6;6;1;2;1;1;0;1077.05374;2216.40413;0.839169585;249.985572;56.3439536;165.253974;1406.97387;232.508023;351.613183;158.221711;0;31306.1664;0;22.1118807;6.14218907;0.40281305;117.30717;0;1;28;0;6;2;0.172094163;0;22.1263742;31.3061664;0;0;0;0 +28.5;0;0;0.6;6;1;2;1;1;0;1076.99977;2218.53406;0.83988161;250.213266;56.3889351;168.421572;1381.77314;232.720405;345.321595;161.51514;0;30544.2177;0;22.8272635;6.34090654;0.3923887;121.102394;0;1;28.5;0;6;2;0.179922892;0;22.8413823;30.5442177;0;0;0;0 +29;0;0;0.6;6;1;2;1;1;0;1076.95132;2220.49319;0.840536505;250.422956;56.4303655;171.486468;1358.20405;232.913614;339.437029;164.697727;0;29834.0289;0;23.5245383;6.53459398;0.382665911;124.801552;0;1;29;0;6;2;0.187757664;0;23.5383076;29.8340289;0;0;0;0 +29.5;0;0;0.6;6;1;2;1;1;0;1076.90713;2222.28389;0.841135097;250.614624;56.4682355;174.455789;1336.09873;233.090158;333.917631;167.777436;0;29170.1502;0;24.2048986;6.72358294;0.373570785;128.410975;0;1;29.5;0;6;2;0.195597936;0;24.2183408;29.1701502;0;0;0;0 +30;0;0;0.6;6;1;2;1;1;0;1076.8666;2223.92555;0.841683877;250.790321;56.5029504;177.335907;1315.31202;233.252051;328.72721;170.761363;0;28547.8807;0;24.8694141;6.90817059;0.365039564;131.93634;0;1;30;0;6;2;0.20344324;0;24.8825496;28.5478807;0;0;0;0 +30.5;0;0;0.6;6;1;2;1;1;0;1076.82931;2225.43555;0.842188646;250.95191;56.5348778;180.132562;1295.71795;233.400994;323.834364;173.655875;0;27963.1509;0;25.5190482;7.0886245;0.357017059;135.382755;0;1;30.5;0;6;2;0.211293164;0;25.5318952;27.9631509;0;0;0;0 +31;0;0;0.6;6;1;2;1;1;0;1076.79488;2226.8287;0.842654359;251.10098;56.5643317;182.850938;1277.20669;233.538442;319.211708;176.466704;0;27412.4197;0;26.1546714;7.2651865;0.34945525;138.754841;0;1;31;0;6;2;0.219147343;0;26.1672465;27.4124197;0;0;0;0 +31.5;0;0;0.6;6;1;2;1;1;0;1076.763;2228.11771;0.843085263;251.238894;56.5915816;185.495739;1259.68198;233.665641;314.835238;179.199035;0;26892.5901;0;26.7770736;7.43807599;0.342312145;142.056787;0;1;31.5;0;6;2;0.227005455;0;26.7893918;26.8925901;0;0;0;0 +32;0;0;0.6;6;1;2;1;1;0;1076.73342;2229.31354;0.84348502;251.366828;56.6168595;188.071245;1243.05907;233.783668;310.683821;181.857568;0;26400.9408;0;27.3869739;7.60749274;0.335550852;145.292409;0;1;32;0;6;2;0.234867209;0;27.399049;26.4009408;0;0;0;0 +32.5;0;0;0.6;6;1;2;1;1;0;1076.70589;2230.4257;0.843856807;251.4858;56.6403668;190.581359;1227.26302;233.893455;306.738766;184.446584;0;25935.0703;0;27.985029;7.77361918;0.329138816;148.46519;0;1;32.5;0;6;2;0.242732346;0;27.9968735;25.9350703;0;0;0;0 +33;0;0;0.6;6;1;2;1;1;0;1076.68022;2231.46244;0.844203388;251.596697;56.6622786;193.029654;1212.22729;233.995815;302.983478;186.969985;0;25492.8506;0;28.5718408;7.93662244;0.323047192;151.578323;0;1;33;0;6;2;0.250600633;0;28.5834662;25.4928506;0;0;0;0 +33.5;0;0;0.6;6;1;2;1;1;0;1076.65623;2232.43102;0.844527181;251.700295;56.6827483;195.41941;1197.89258;234.09146;299.403165;189.431346;0;25072.3885;0;29.1479618;8.09665605;0.317250323;154.63474;0;1;33.5;0;6;2;0.25847186;0;29.1593787;25.0723885;0;0;0;0 +34;0;0;0.6;6;1;2;1;1;0;1076.63376;2233.33778;0.84483031;251.797275;56.7019103;197.753639;1184.20584;234.181014;295.984594;191.833947;0;24671.9941;0;29.7139012;8.25386145;0.311725303;157.637142;0;1;34;0;6;2;0.266345836;0;29.7251195;24.6719941;0;0;0;0 +34.5;0;0;0.6;6;1;2;1;1;0;1076.61268;2234.18832;0.845114647;251.888236;56.7198833;200.035123;1171.11947;234.265027;292.715891;194.180806;0;24290.1534;0;30.2701293;8.40836925;0.306451618;160.588023;0;1;34.5;0;6;2;0.274222387;0;30.2811578;24.2901534;0;0;0;0 +35;0;0;0.6;6;1;2;1;1;0;1076.59286;2234.98759;0.845381847;251.97371;56.7367719;202.26643;1158.59061;234.343988;289.586363;196.474703;0;23925.5058;0;30.8170811;8.56030031;0.301410832;163.489693;0;1;35;0;6;2;0.282101357;0;30.8279284;23.9255058;0;0;0;0 +35.5;0;0;0.6;6;1;2;1;1;0;1076.5742;2235.74;0.84563338;252.054167;56.7526694;204.449939;1146.58057;234.418327;286.586354;198.718211;0;23576.8248;0;31.3551605;8.7097668;0.296586327;166.344293;0;1;35.5;0;6;2;0.289982601;0;31.3658342;23.5768248;0;0;0;0 +36;0;0;0.6;6;1;2;1;1;0;1076.5566;2236.44945;0.845870554;252.130027;56.7676586;206.587855;1135.05429;234.488432;283.70712;200.913712;0;23243.0018;0;31.8847426;8.85687294;0.291963083;169.153813;0;1;36;0;6;2;0.297865988;0;31.89525;23.2430018;0;0;0;0 +36.5;0;0;0.6;6;1;2;1;1;0;1076.53997;2237.11945;0.846094539;252.201665;56.7818136;208.682231;1123.97996;234.554645;280.940719;203.063416;0;22923.0318;0;32.4061771;9.00171586;0.287527486;171.920109;0;1;36.5;0;6;2;0.305751397;0;32.416525;22.9230318;0;0;0;0 +37;0;0;0.6;6;1;2;1;1;0;1076.52424;2237.75312;0.846306382;252.269416;56.7952006;210.734977;1113.32859;234.617275;278.279924;205.169378;0;22616.0016;0;32.9197902;9.14438616;0.283267164;174.644911;0;1;37;0;6;2;0.313638717;0;32.9299848;22.6160016;0;0;0;0 +37.5;0;0;0.6;6;1;2;1;1;0;1076.50933;2238.35329;0.846507024;252.333582;56.8078791;212.747876;1103.07377;234.6766;275.718136;207.233513;0;22321.079;0;33.4258869;9.28496857;0.279170846;177.329837;0;1;37.5;0;6;2;0.321527845;0;33.4359341;22.321079;0;0;0;0 +38;0;0;0.6;6;1;2;1;1;0;1076.4952;2238.92249;0.846697313;252.394434;56.8199029;214.722593;1093.19129;234.732869;273.249322;209.257606;0;22037.5044;0;33.9247528;9.42354243;0.275228242;179.976403;0;1;38;0;6;2;0.329418687;0;33.9346582;22.0375044;0;0;0;0 +38.5;0;0;0.6;6;1;2;1;1;0;1076.48177;2239.463;0.846878012;252.452217;56.8313203;216.660687;1083.65902;234.786308;270.86795;211.243328;0;21764.5824;0;34.4166558;9.56018216;0.271429935;182.58603;0;1;38.5;0;6;2;0.337311156;0;34.4264246;21.7645824;0;0;0;0 +39;0;0;0.6;6;1;2;1;1;0;1076.469;2239.9769;0.847049816;252.507153;56.8421753;218.563621;1074.45658;234.83712;268.56894;213.192242;0;21501.6753;0;34.9018476;9.69495767;0.267767289;185.160052;0;1;39;0;6;2;0.34520517;0;34.9114847;21.5016753;0;0;0;0 +39.5;0;0;0.6;6;1;2;1;1;0;1076.45684;2240.46607;0.847213353;252.559444;56.8525075;220.432765;1065.56523;234.885491;266.347615;215.105813;0;21248.1973;0;35.3805651;9.82793475;0.264232369;187.699728;0;1;39.5;0;6;2;0.353100654;0;35.390075;21.2481973;0;0;0;0 +40;0;0;0.6;6;1;2;1;1;0;1076.44526;2240.93222;0.847369193;252.609272;56.8623531;222.26941;1056.96771;234.93159;264.199661;216.985417;0;21003.6088;0;35.8530313;9.95917537;0.260817869;190.206239;0;1;40;0;6;2;0.360997539;0;35.8624183;21.0036088;0;0;0;0 +40.5;0;0;1;10;1;2;1;1;0;1172.63358;2684.46141;0.999213375;329.646226;73.0433652;228.251178;1088.28867;248.403171;272.38383;222.803502;0;21938.2433;0;36.3288303;10.0913418;0.27281478;192.730431;0;1;40.5;0;10;2;0.370414445;0;36.338669;21.9382433;0;0;0;0 +41;0;0;1;10;1;2;1;1;0;1286.95163;2612.61091;0.999995946;352.099639;80.2270225;237.35181;1151.59919;273.334152;288.19183;231.587974;0;23763.3163;0;36.8427192;10.2340887;0.29742583;195.456696;0;1;41;0;10;2;0.381084315;0;36.8534429;23.7633163;0;0;0;0 +41.5;0;0;1;10;1;2;1;1;0;1376.1698;2509.32554;0.999999979;361.624336;85.7891223;245.208679;1200.91287;294.474259;300.42507;239.200177;0;25192.2821;0;37.3969709;10.3880475;0.316787743;198.397093;0;1;41.5;0;10;2;0.392641681;0;37.4083865;25.1922821;0;0;0;0 +42;0;0;1;10;1;2;1;1;0;1433.75252;2250.50874;0.937162247;337.896362;83.7624147;251.40514;1230.80561;309.430857;307.777827;245.249584;0;26057.6443;0;37.97918;10.5497722;0.328557431;201.485808;0;1;42;0;10;2;0.404756023;0;37.9910124;26.0576443;0;0;0;0 +42.5;0;0;1;10;1;2;1;1;0;1430.48653;2238.76375;0.930991359;335.367256;83.0213195;254.38749;1224.88894;311.596423;306.170549;248.264079;0;25868.0791;0;38.5695803;10.7137723;0.326059551;204.617979;0;1;42.5;0;10;2;0.416200791;0;38.5813154;25.8680791;0;0;0;0 +43;0;0;1;10;1;2;1;1;0;1430.2223;2238.23454;0.930648341;335.226048;82.9754014;256.857008;1213.71543;311.751314;303.373355;250.789541;0;25538.705;0;39.1523051;10.8756403;0.321470583;207.70943;0;1;43;0;10;2;0.427718313;0;39.1638748;25.538705;0;0;0;0 +43.5;0;0;1;10;1;2;1;1;0;1430.18815;2238.73625;0.930813051;335.293184;82.9881049;259.264134;1202.751;311.830198;300.633917;253.251456;0;25216.7961;0;39.7268019;11.0352228;0.316973832;210.75723;0;1;43.5;0;10;2;0.439242889;0;39.7382098;25.2167961;0;0;0;0 +44;0;0;1;10;1;2;1;1;0;1430.17248;2239.27543;0.931001906;335.370261;83.0040331;261.626589;1192.16616;311.902366;297.989565;255.666798;0;24906.6946;0;40.2933497;11.1925971;0.312636534;213.762859;0;1;44;0;10;2;0.450770105;0;40.3046016;24.9066946;0;0;0;0 +44.5;0;0;1;10;1;2;1;1;0;1430.15887;2239.79117;0.931183329;335.444311;83.0194181;263.946736;1181.94636;311.970884;295.436387;258.038008;0;24607.8853;0;40.852232;11.3478422;0.30845217;216.727822;0;1;44.5;0;10;2;0.462299539;0;40.8633333;24.6078853;0;0;0;0 +45;0;0;1;10;1;2;1;1;0;1430.14601;2240.28235;0.931356164;335.514855;83.0340805;266.22623;1172.07133;312.03613;292.969302;260.366844;0;24319.7207;0;41.403716;11.5010322;0.304412005;219.653536;0;1;45;0;10;2;0.47383106;0;41.414672;24.3197207;0;0;0;0 +45.5;0;0;1;10;1;2;1;1;0;1430.13376;2240.75058;0.931520926;335.582104;83.0480582;268.466602;1162.52202;312.098335;290.583557;262.654931;0;24041.5969;0;41.9480538;11.6522372;0.300507861;222.541337;0;1;45.5;0;10;2;0.485364569;0;41.9588693;24.0415969;0;0;0;0 +46;0;0;1;10;1;2;1;1;0;1430.12206;2241.19741;0.931678155;335.646279;83.0613965;270.669293;1153.28082;312.157703;288.274755;264.903798;0;23772.9556;0;42.4854832;11.8015231;0.296732184;225.392489;0;1;46;0;10;2;0.496899974;0;42.4961629;23.7729556;0;0;0;0 +46.5;0;0;1;10;1;2;1;1;0;1430.11089;2241.62425;0.931828348;335.707581;83.0741376;272.835662;1144.33142;312.21442;286.038826;267.114885;0;23513.2811;0;43.0162288;11.9489525;0.293077988;228.208182;0;1;46.5;0;10;2;0.508437188;0;43.0267771;23.5132811;0;0;0;0 +47;0;0;1;10;1;2;1;1;0;1430.10021;2242.03239;0.931971959;335.766196;83.08632;274.966991;1135.6587;312.268657;283.871993;269.289552;0;23262.0955;0;43.5405035;12.0945843;0.289538806;230.989545;0;1;47;0;10;2;0.519976132;0;43.5509243;23.2620955;0;0;0;0 +47.5;0;0;1;10;1;2;1;1;0;1430.08998;2242.42302;0.932109402;335.822294;83.0979789;277.064495;1127.24862;312.32057;281.770751;271.42908;0;23018.9556;0;44.0585085;12.2384746;0.286108639;233.737647;0;1;47.5;0;10;2;0.53151673;0;44.068806;23.0189556;0;0;0;0 +48;0;0;1;10;1;2;1;1;0;1430.08018;2242.7972;0.93224106;335.87603;83.1091468;279.129321;1119.0881;312.370302;279.731838;273.534684;0;22783.4497;0;44.570435;12.3806764;0.282781913;236.453502;0;1;48;0;10;2;0.543058913;0;44.5806128;22.7834497;0;0;0;0 +48.5;0;0;1;10;1;2;1;1;0;1430.07078;2243.15595;0.932367284;335.927548;83.1198536;281.162556;1111.16498;312.417986;277.752216;275.607511;0;22555.1947;0;45.0764644;12.5212401;0.279553443;239.138071;0;1;48.5;0;10;2;0.554602613;0;45.086526;22.5551947;0;0;0;0 +49;0;0;1;10;1;2;1;1;0;1430.06177;2243.50018;0.932488398;335.976981;83.1301267;283.165231;1103.4679;312.463744;275.829048;277.64865;0;22333.8337;0;45.5767689;12.6602136;0.2764184;241.792269;0;1;49;0;10;2;0.56614777;0;45.5867178;22.3338337;0;0;0;0 +49.5;0;0;1;10;1;2;1;1;0;1430.05311;2243.83075;0.932604702;336.02445;83.1399916;285.138327;1095.98626;312.507688;273.959689;279.659133;0;22119.0336;0;46.0715127;12.7976424;0.273372275;244.416966;0;1;49.5;0;10;2;0.577694324;0;46.0813519;22.1190336;0;0;0;0 +50;0;0;1;10;1;2;1;1;0;1430.04478;2244.14843;0.932716471;336.070068;83.1494716;287.082772;1088.71013;312.549923;272.14166;281.639939;0;21910.4836;0;46.5608518;12.9335699;0.270410861;247.012991;0;1;50;0;10;2;0.589242221;0;46.5705845;21.9104836;0;0;0;0 +50.5;0;0;1;10;1;2;1;1;0;1430.03678;2244.45395;0.932823962;336.11394;83.1585886;288.999453;1081.63022;312.590543;270.372642;283.592;0;21707.8925;0;47.0449351;13.0680375;0.267530219;249.581132;0;1;50.5;0;10;2;0.600791409;0;47.0545641;21.7078925;0;0;0;0 +51;0;0;1;10;1;2;1;1;0;1430.02907;2244.74799;0.932927411;336.156162;83.1673626;290.889211;1074.73783;312.62964;268.65046;285.516202;0;21510.9879;0;47.5239045;13.2010846;0.264726662;252.122144;0;1;51;0;10;2;0.612341838;0;47.5334327;21.5109879;0;0;0;0 +51.5;0;0;1;10;1;2;1;1;0;1430.02165;2245.03117;0.933027038;336.196824;83.1758124;292.752848;1068.02477;312.667295;266.973072;287.413387;0;21319.5143;0;47.9978957;13.3327488;0.261996733;254.636745;0;1;51.5;0;10;2;0.623893463;0;48.0073256;21.3195143;0;0;0;0 +52;0;0;1;10;1;2;1;1;0;1430.0145;2245.30407;0.933123049;336.23601;83.1839553;294.591131;1061.48336;312.703585;265.338559;289.28436;0;21133.2319;0;48.467038;13.4630661;0.259337187;257.125622;0;1;52;0;10;2;0.635446239;0;48.4763722;21.1332319;0;0;0;0 +52.5;0;0;1;10;1;2;1;1;0;1430.0076;2245.56724;0.933215633;336.273797;83.1918075;296.404789;1055.10638;312.738583;263.745117;291.129886;0;20951.9154;0;48.9314554;13.5920709;0.256744976;259.589433;0;1;52.5;0;10;2;0.647000126;0;48.9406964;20.9519154;0;0;0;0 +53;0;0;1;10;1;2;1;1;0;1430.00094;2245.82118;0.933304969;336.310258;83.199384;298.194519;1048.887;312.772354;262.191044;292.950698;0;20775.3527;0;49.3912664;13.7197962;0.254217231;262.028807;0;1;53;0;10;2;0.658555083;0;49.4004165;20.7753527;0;0;0;0 +53.5;0;0;1;10;1;2;1;1;0;1429.99451;2246.06635;0.933391221;336.345461;83.2066988;299.960988;1042.81882;312.804963;260.674738;294.747494;0;20603.3443;0;49.8465846;13.8462735;0.251751251;264.444345;0;1;53.5;0;10;2;0.670111074;0;49.8556459;20.6033443;0;0;0;0 +54;0;0;1;10;1;2;1;1;0;1429.9883;2246.3032;0.933474544;336.379468;83.2137652;301.704834;1036.89577;312.836466;259.194687;296.520941;0;20435.702;0;50.2975188;13.971533;0.249344491;266.836625;0;1;54;0;10;2;0.681668064;0;50.3064934;20.435702;0;0;0;0 +54.5;0;0;1;10;1;2;1;1;0;1429.9823;2246.53214;0.933555082;336.412339;83.2205954;303.426667;1031.11213;312.866918;257.749461;298.271678;0;20272.2482;0;50.7441733;14.0956037;0.24699455;269.2062;0;1;54.5;0;10;2;0.693226017;0;50.7530634;20.2722482;0;0;0;0 +55;0;0;1;10;1;2;1;1;0;1429.97649;2246.75355;0.933632972;336.444128;83.2272008;305.127072;1025.4625;312.89637;256.33771;300.000317;0;20112.8154;0;51.1866483;14.2185134;0.244699159;271.553603;0;1;55;0;10;2;0.704784904;0;51.1954558;20.1128154;0;0;0;0 +55.5;0;0;1;10;1;2;1;1;0;1429.97087;2246.96779;0.933708339;336.474888;83.2335922;306.806607;1019.94176;312.92487;254.958156;301.707444;0;19957.245;0;51.62504;14.3402889;0.242456175;273.879343;0;1;55.5;0;10;2;0.716344692;0;51.6337668;19.957245;0;0;0;0 +56;0;0;1;10;1;2;1;1;0;1429.96543;2247.17521;0.933781303;336.504667;83.2397797;308.465811;1014.54505;312.952462;253.609588;303.393619;0;19805.3872;0;52.0594409;14.4609558;0.240263571;276.183912;0;1;56;0;10;2;0.727905354;0;52.0680888;19.8053872;0;0;0;0 +56.5;0;0;1;10;1;2;1;1;0;1429.96016;2247.3761;0.933851975;336.53351;83.2457728;310.105199;1009.26779;312.97919;252.290858;305.059382;0;19657.0999;0;52.4899399;14.5805388;0.238119426;278.46778;0;1;56.5;0;10;2;0.739466861;0;52.4985106;19.6570999;0;0;0;0 +57;0;0;1;10;1;2;1;1;0;1429.95506;2247.57079;0.933920459;336.561461;83.2515803;311.725267;1004.10562;313.005091;251.00088;306.705249;0;19512.2488;0;52.9166225;14.6990618;0.236021921;280.731401;0;1;57;0;10;2;0.751029188;0;52.9251177;19.5122488;0;0;0;0 +57.5;0;0;1;10;1;2;1;1;0;1429.9501;2247.75954;0.933986856;336.588559;83.2572106;313.326488;999.054391;313.030204;249.738618;308.331716;0;19370.7061;0;53.339571;14.8165475;0.233969331;282.975213;0;1;57.5;0;10;2;0.762592308;0;53.3479925;19.3707061;0;0;0;0 +58;0;0;1;10;1;2;1;1;0;1429.9453;2247.94261;0.934051256;336.614843;83.2626717;314.909322;994.110182;313.054563;248.503091;309.93926;0;19232.351;0;53.7588649;14.933018;0.231960019;285.199636;0;1;58;0;10;2;0.774156198;0;53.767214;19.232351;0;0;0;0 +58.5;0;0;1;10;1;2;1;1;0;1429.94064;2248.12026;0.934113749;336.640348;83.2679709;316.474207;989.269252;313.078202;247.293365;311.52834;0;19097.0682;0;54.1745805;15.0484946;0.229992428;287.405076;0;1;58.5;0;10;2;0.785720835;0;54.1828589;19.0970682;0;0;0;0 +59;0;0;1;10;1;2;1;1;0;1429.93611;2248.29273;0.934174415;336.665108;83.2731153;318.021567;984.528044;313.101151;246.108553;313.099396;0;18964.7487;0;54.5867917;15.1629977;0.228065082;289.591924;0;1;59;0;10;2;0.797286198;0;54.5950007;18.9647487;0;0;0;0 +59.5;0;0;1;10;1;2;1;1;0;1429.93172;2248.46023;0.934233334;336.689154;83.2781113;319.551809;979.883172;313.12344;244.947809;314.652853;0;18835.2883;0;54.9955695;15.2765471;0.226176573;291.760558;0;1;59.5;0;10;2;0.808852264;0;55.0037105;18.8352883;0;0;0;0 +60;0;0;1;10;1;2;1;1;0;1429.92744;2248.62296;0.934290579;336.712517;83.2829654;321.065325;975.331409;313.145096;243.810325;316.189119;0;18708.5881;0;55.4009825;15.3891618;0.224325565;293.911341;0;1;60;0;10;2;0.820419013;0;55.4090569;18.7085881;0;0;0;0 +60.5;0;0;1;10;1;2;1;1;0;1429.92329;2248.78114;0.934346218;336.735225;83.2876833;322.562495;970.869679;313.166146;242.695336;317.708589;0;18584.5538;0;55.8030972;15.5008603;0.222510781;296.044626;0;1;60.5;0;10;2;0.831986428;0;55.8111063;18.5845538;0;0;0;0 +61;0;0;1;10;1;2;1;1;0;1429.91926;2248.93494;0.934400319;336.757305;83.2922706;324.043684;966.495047;313.186615;241.602106;319.211641;0;18463.0957;0;56.2019777;15.6116605;0.220731008;298.160752;0;1;61;0;10;2;0.843554488;0;56.2099227;18.4630957;0;0;0;0 +61.5;0;0;1;10;1;2;1;1;0;1429.91533;2249.08455;0.934452942;336.778782;83.2967327;325.509242;962.204711;313.206526;240.529936;320.698643;0;18344.128;0;56.5976857;15.7215794;0.218985087;300.260048;0;1;61.5;0;10;2;0.855123176;0;56.6055679;18.344128;0;0;0;0 +62;0;0;1;10;1;2;1;1;0;1429.91151;2249.23012;0.934504148;336.79968;83.3010745;326.95951;957.995994;313.225901;239.478159;322.169947;0;18227.569;0;56.9902814;15.8306337;0.217271912;302.342833;0;1;62;0;10;2;0.866692476;0;56.9981019;18.227569;0;0;0;0 +62.5;0;0;1;10;1;2;1;1;0;1429.90779;2249.37182;0.934553991;336.820022;83.3053008;328.394816;953.866339;313.244761;238.446134;323.625893;0;18113.3405;0;57.3798225;15.9388396;0.215590428;304.409413;0;1;62.5;0;10;2;0.87826237;0;57.3875826;18.1133405;0;0;0;0 +63;0;0;1;10;1;2;1;1;0;1429.90416;2249.5098;0.934602525;336.83983;83.309416;329.815478;949.813299;313.263127;237.433251;325.066813;0;18001.368;0;57.7663653;16.0462126;0.213939626;306.460086;0;1;63;0;10;2;0.889832843;0;57.774066;18.001368;0;0;0;0 +63.5;0;0;1;10;1;2;1;1;0;1429.90063;2249.64421;0.934649801;336.859124;83.3134244;331.221801;945.834532;313.281017;236.438925;326.493022;0;17891.5802;0;58.1499641;16.1527678;0.212318542;308.495141;0;1;63.5;0;10;2;0.901403881;0;58.1576064;17.8915802;0;0;0;0 +64;0;0;1;10;1;2;1;1;0;1429.8972;2249.77517;0.934695865;336.877924;83.3173302;332.614081;941.927797;313.298449;235.462595;327.904829;0;17783.9088;0;58.5306715;16.2585199;0.210726253;310.514857;0;1;64;0;10;2;0.912975468;0;58.5382565;17.7839088;0;0;0;0 +64.5;0;0;1;10;1;2;1;1;0;1429.89384;2249.90282;0.934740764;336.896248;83.3211371;333.992607;938.090947;313.315441;234.503727;329.302532;0;17678.2886;0;58.9085386;16.3634829;0.209161878;312.519503;0;1;64.5;0;10;2;0.92454759;0;58.9160673;17.6782886;0;0;0;0 +65;0;0;1;10;1;2;1;1;0;1429.89058;2250.02728;0.93478454;336.914114;83.3248488;335.357654;934.321924;313.332008;233.561805;330.686418;0;17574.657;0;59.2836147;16.4676707;0.20762457;314.509344;0;1;65;0;10;2;0.936120234;0;59.2910881;17.574657;0;0;0;0 +65.5;0;0;1;10;1;2;1;1;0;1429.88739;2250.14866;0.934827235;336.931539;83.3284687;336.709491;930.618754;313.348167;232.636337;332.056764;0;17472.9541;0;59.6559479;16.5710966;0.206113521;316.484633;0;1;65.5;0;10;2;0.947693387;0;59.6633669;17.4729541;0;0;0;0 +66;0;0;1;10;1;2;1;1;0;1429.88428;2250.26708;0.934868888;336.948538;83.3320003;338.048379;926.979544;313.363932;231.72685;333.413842;0;17373.1226;0;60.0255847;16.6737735;0.204627954;318.445617;0;1;66;0;10;2;0.959267037;0;60.0329503;17.3731226;0;0;0;0 +66.5;0;0;1;10;1;2;1;1;0;1429.88124;2250.38265;0.934909536;336.965127;83.3354467;339.374568;923.402477;313.379317;230.832889;334.757911;0;17275.1072;0;60.3925703;16.775714;0.203167125;320.392537;0;1;66.5;0;10;2;0.970841171;0;60.3998833;17.2751072;0;0;0;0 +67;0;0;1;10;1;2;1;1;0;1429.87828;2250.49546;0.934949214;336.981321;83.3388108;340.688304;919.885806;313.394336;229.954019;336.089224;0;17178.8554;0;60.7569486;16.8769302;0.20173032;322.325623;0;1;67;0;10;2;0.982415778;0;60.7642099;17.1788554;0;0;0;0 +67.5;0;0;1;10;1;2;1;1;0;1429.87539;2250.60561;0.934987958;336.997132;83.3420957;341.989824;916.427856;313.409001;229.089821;337.408027;0;17084.3162;0;61.1187622;16.9774339;0.200316853;324.245104;0;1;67.5;0;10;2;0.993990846;0;61.1259726;17.0843162;0;0;0;0 +68;0;0;1;10;1;2;1;1;0;1429.87256;2250.71319;0.935025798;337.012576;83.3453039;343.279355;913.027013;313.423324;228.239892;338.714557;0;16991.441;0;61.4780524;17.0772368;0.198926067;326.151198;0;1;68;0;10;2;1.00556637;0;61.4852128;16.991441;0;0;0;0 +68.5;0;0;1;10;1;2;1;1;0;1429.8698;2250.8183;0.935062766;337.027663;83.3484382;344.557122;909.681727;313.437318;227.403844;340.009045;0;16900.183;0;61.8348596;17.1763499;0.197557328;328.044119;0;1;68.5;0;10;2;1.01714232;0;61.8419707;16.900183;0;0;0;0 +69;0;0;1;10;1;2;1;1;0;1429.8671;2250.92101;0.935098891;337.042406;83.3515009;345.823341;906.390508;313.450993;226.581306;341.291715;0;16810.4972;0;62.1892228;17.2747841;0.196210028;329.924074;0;1;69;0;10;2;1.02871871;0;62.1962854;16.8104972;0;0;0;0 +69.5;0;0;1;10;1;2;1;1;0;1429.86446;2251.0214;0.935134201;337.056817;83.3544947;347.07822;903.151919;313.464361;225.771918;342.562782;0;16722.3404;0;62.5411801;17.37255;0.194883584;331.791265;0;1;69.5;0;10;2;1.04029552;0;62.5481949;16.7223404;0;0;0;0 +70;0;0;1;10;1;2;1;1;0;1429.86188;2251.11956;0.935168725;337.070906;83.3574216;348.321965;899.964577;313.47743;224.975336;343.822458;0;16635.6708;0;62.8907685;17.4696579;0.193577431;333.645889;0;1;70;0;10;2;1.05187275;0;62.8977363;16.6356708;0;0;0;0 +70.5;0;0;1;10;1;2;1;1;0;1429.85936;2251.21555;0.935202486;337.084685;83.3602839;349.554773;896.827153;313.490212;224.191226;345.070948;0;16550.4486;0;63.238024;17.5661178;0.19229103;335.488137;0;1;70.5;0;10;2;1.06345037;0;63.2449456;16.5504486;0;0;0;0 +71;0;0;1;10;1;2;1;1;0;1429.8569;2251.30945;0.93523551;337.098163;83.3630838;350.776836;893.738361;313.502714;223.419268;346.308451;0;16466.635;0;63.5829818;17.6619394;0.191023859;337.318195;0;1;71;0;10;2;1.07502839;0;63.5898577;16.466635;0;0;0;0 +71.5;0;0;1;10;1;2;1;1;0;1429.85448;2251.40131;0.935267821;337.111349;83.3658231;351.988341;890.696965;313.514947;222.659153;347.535158;0;16384.1929;0;63.9256759;17.7571322;0.189775416;339.136244;0;1;71.5;0;10;2;1.08660679;0;63.9325069;16.3841929;0;0;0;0 +72;0;0;1;10;1;2;1;1;0;1429.85212;2251.49122;0.935299442;337.124254;83.3685039;353.189471;887.701772;313.526919;221.910583;348.751259;0;16303.0866;0;64.2661396;17.8517054;0.188545217;340.94246;0;1;72;0;10;2;1.09818556;0;64.2729264;16.3030866;0;0;0;0 +72.5;0;0;1;10;1;2;1;1;0;1429.84981;2251.57922;0.935330393;337.136886;83.3711279;354.380402;884.75163;313.538638;221.17327;349.956936;0;16223.2815;0;64.6044054;17.9456682;0.187332795;342.737015;0;1;72.5;0;10;2;1.10976471;0;64.6111485;16.2232815;0;0;0;0 +73;0;0;1;10;1;2;1;1;0;1429.84754;2251.66538;0.935360696;337.149253;83.3736969;355.561305;881.845429;313.550112;220.446936;351.152366;0;16144.7442;0;64.9405047;18.0390291;0.186137701;344.520078;0;1;73;0;10;2;1.12134421;0;64.9472048;16.1447442;0;0;0;0 +73.5;0;0;1;10;1;2;1;1;0;1429.84533;2251.74975;0.935390371;337.161363;83.3762127;356.732349;878.982096;313.561348;219.731315;352.337723;0;16067.4428;0;65.2744685;18.1317968;0.184959501;346.291811;0;1;73.5;0;10;2;1.13292407;0;65.2811262;16.0674428;0;0;0;0 +74;0;0;1;10;1;2;1;1;0;1429.84315;2251.83239;0.935419436;337.173225;83.3786768;357.893696;876.160594;313.572353;219.026146;353.513173;0;15991.3462;0;65.6063267;18.2239796;0.183797776;348.052373;0;1;74;0;10;2;1.14450428;0;65.6129426;15.9913462;0;0;0;0 +74.5;0;0;1;10;1;2;1;1;0;1429.84103;2251.91335;0.935447911;337.184846;83.3810909;359.045505;873.379923;313.583135;218.33118;354.678881;0;15916.4245;0;65.9361086;18.3155857;0.182652123;349.80192;0;1;74.5;0;10;2;1.15608482;0;65.9426833;15.9164245;0;0;0;0 +75;0;0;1;10;1;2;1;1;0;1429.83894;2251.99269;0.935475813;337.196233;83.3834563;360.18793;870.639114;313.593701;217.646175;355.835007;0;15842.6488;0;66.2638428;18.406623;0.18152215;351.540604;0;1;75;0;10;2;1.1676657;0;66.2703768;15.8426488;0;0;0;0 +75.5;0;0;1;10;1;2;1;1;0;1429.8369;2252.07044;0.935503159;337.207394;83.3857746;361.321123;867.937234;313.604056;216.970897;356.981705;0;15769.9912;0;66.5895571;18.4970992;0.180407481;353.268573;0;1;75.5;0;10;2;1.1792469;0;66.596051;15.7699912;0;0;0;0 +76;0;0;1;10;1;2;1;1;0;1429.83489;2252.14666;0.935529965;337.218333;83.3880471;362.445229;865.273376;313.614207;216.30512;358.119127;0;15698.4247;0;66.9132788;18.5870219;0.179307751;354.985969;0;1;76;0;10;2;1.19082842;0;66.9197331;15.6984247;0;0;0;0 +76.5;0;0;1;10;1;2;1;1;0;1429.83293;2252.22139;0.935556247;337.229059;83.3902752;363.560392;862.646666;313.62416;215.648626;359.247419;0;15627.9232;0;67.2350345;18.6763985;0.178222608;356.692936;0;1;76.5;0;10;2;1.20241025;0;67.2414497;15.6279232;0;0;0;0 +77;0;0;1;10;1;2;1;1;0;1429.831;2252.29467;0.93558202;337.239578;83.3924601;364.66675;860.056256;313.63392;215.001203;360.366726;0;15558.4615;0;67.55485;18.7652361;0.177151711;358.38961;0;1;77;0;10;2;1.21399239;0;67.5612267;15.5584615;0;0;0;0 +77.5;0;0;1;10;1;2;1;1;0;1429.82911;2252.36654;0.935607298;337.249894;83.3946031;365.764441;857.501326;313.643493;214.362646;361.477188;0;15490.0153;0;67.8727507;18.8535419;0.176094732;360.076126;0;1;77.5;0;10;2;1.22557482;0;67.8790893;15.4900153;0;0;0;0 +78;0;0;1;10;1;2;1;1;0;1429.82726;2252.43705;0.935632096;337.260014;83.3967054;366.853595;854.981083;313.652884;213.732757;362.57894;0;15422.5609;0;68.1887613;18.9413226;0.17505135;361.752614;0;1;78;0;10;2;1.23715756;0;68.1950624;15.4225609;0;0;0;0 +78.5;0;0;1;10;1;2;1;1;0;1429.82544;2252.50624;0.935656428;337.269944;83.398768;367.934344;852.494757;313.662099;213.111343;363.672117;0;15356.0755;0;68.5029062;19.0285851;0.174021258;363.419205;0;1;78.5;0;10;2;1.24874058;0;68.5091702;15.3560755;0;0;0;0 +79;0;0;1;10;1;2;1;1;0;1429.82366;2252.57413;0.935680305;337.279689;83.4007922;369.006812;850.041602;313.671142;212.498218;364.756847;0;15290.5369;0;68.8152088;19.1153358;0.173004157;365.076021;0;1;79;0;10;2;1.26032389;0;68.8214362;15.2905369;0;0;0;0 +79.5;0;0;1;10;1;2;1;1;0;1429.82191;2252.64076;0.93570374;337.289253;83.4027789;370.071123;847.620898;313.680017;211.893202;365.833259;0;15225.9238;0;69.1256924;19.2015812;0.171999757;366.723188;0;1;79.5;0;10;2;1.27190747;0;69.1318837;15.2259238;0;0;0;0 +80;0;0;1;10;1;2;1;1;0;1429.82019;2252.70618;0.935726746;337.298642;83.4047291;371.127397;845.231942;313.688731;211.29612;366.901475;0;15162.2153;0;69.4343795;19.2873276;0.171007778;368.360824;0;1;80;0;10;2;1.28349133;0;69.440535;15.1622153;0;0;0;0 +80.5;0;0;1;10;1;2;1;1;0;1429.8185;2252.77041;0.935749334;337.30786;83.406644;372.175752;842.874056;313.697286;210.706803;367.961616;0;15099.3915;0;69.7412922;19.3725812;0.170027949;369.989046;0;1;80.5;0;10;2;1.29507545;0;69.7474125;15.0993915;0;0;0;0 +81;0;0;1;10;1;2;1;1;0;1429.81684;2252.83348;0.935771515;337.316912;83.4085244;373.216302;840.546582;313.705687;210.125085;369.0138;0;15037.4327;0;70.0464522;19.4573478;0.169060006;371.60797;0;1;81;0;10;2;1.30665984;0;70.0525376;15.0374327;0;0;0;0 +81.5;0;0;1;10;1;2;1;1;0;1429.81521;2252.89542;0.9357933;337.325803;83.4103711;374.249159;838.24888;313.713938;209.550806;370.058143;0;14976.3201;0;70.3498805;19.5416335;0.168103694;373.217708;0;1;81.5;0;10;2;1.31824449;0;70.3559316;14.9763201;0;0;0;0 +82;0;0;1;10;1;2;1;1;0;1429.81361;2252.95627;0.9358147;337.334537;83.4121853;375.274433;835.98033;313.722044;208.983813;371.094756;0;14916.0354;0;70.6515981;19.6254439;0.167158766;374.818369;0;1;82;0;10;2;1.32982939;0;70.6576151;14.9160354;0;0;0;0 +82.5;0;0;1;10;1;2;1;1;0;1429.81204;2253.01605;0.935835725;337.343117;83.4139675;376.29223;833.740329;313.730007;208.423954;372.123751;0;14856.5607;0;70.951625;19.7087847;0.166224981;376.410061;0;1;82.5;0;10;2;1.34141455;0;70.9576084;14.8565607;0;0;0;0 +83;0;0;1;10;1;2;1;1;0;1429.81049;2253.07479;0.935856383;337.351548;83.4157188;377.302655;831.528293;313.737832;207.871083;373.145233;0;14797.8789;0;71.2499811;19.7916614;0.165302107;377.99289;0;1;83;0;10;2;1.35299994;0;71.2559313;14.7978789;0;0;0;0 +83.5;0;0;1;10;1;2;1;1;0;1429.80898;2253.13252;0.935876685;337.359833;83.4174398;378.30581;829.343654;313.745523;207.325059;374.159309;0;14739.9732;0;71.5466859;19.8740794;0.164389918;379.566958;0;1;83.5;0;10;2;1.36458558;0;71.5526033;14.7399732;0;0;0;0 +84;0;0;1;10;1;2;1;1;0;1429.80748;2253.18926;0.935896641;337.367977;83.4191314;379.301795;827.18586;313.753081;206.785743;375.16608;0;14682.8273;0;71.8417585;19.956044;0.163488193;381.132366;0;1;84;0;10;2;1.37617146;0;71.8476434;14.6828273;0;0;0;0 +84.5;0;0;1;10;1;2;1;1;0;1429.80602;2253.24504;0.935916257;337.375983;83.4207943;380.290708;825.054374;313.760512;206.253002;376.165648;0;14626.4254;0;72.1352173;20.0375604;0.16259672;382.689214;0;1;84.5;0;10;2;1.38775757;0;72.1410702;14.6264254;0;0;0;0 +85;0;0;1;10;1;2;1;1;0;1429.80458;2253.29988;0.935935544;337.383854;83.4224293;381.272644;822.948677;313.767818;205.726706;377.15811;0;14570.7523;0;72.4270808;20.1186335;0.161715292;384.237597;0;1;85;0;10;2;1.39934391;0;72.4329019;14.5707523;0;0;0;0 +85.5;0;0;1;10;1;2;1;1;0;1429.80316;2253.35381;0.935954508;337.391593;83.4240369;382.247697;820.86826;313.775002;205.206727;378.143562;0;14515.793;0;72.7173667;20.1992685;0.160843707;385.777612;0;1;85.5;0;10;2;1.41093047;0;72.7231565;14.515793;0;0;0;0 +86;0;0;1;10;1;2;1;1;0;1429.80176;2253.40684;0.935973159;337.399205;83.4256179;383.215958;818.812632;313.782067;204.692942;379.122099;0;14461.533;0;73.0060927;20.2794702;0.15998177;387.309351;0;1;86;0;10;2;1.42251725;0;73.0118514;14.461533;0;0;0;0 +86.5;0;0;1;10;1;2;1;1;0;1429.80039;2253.459;0.935991504;337.406691;83.427173;384.177517;816.781312;313.789016;204.185233;380.093812;0;14407.9583;0;73.2932759;20.3592433;0.159129292;388.832905;0;1;86.5;0;10;2;1.43410425;0;73.2990039;14.4079583;0;0;0;0 +87;0;0;1;10;1;2;1;1;0;1429.79904;2253.51032;0.93600955;337.414056;83.4287027;385.132461;814.773836;313.795853;203.683482;381.058792;0;14355.0552;0;73.5789332;20.4385926;0.158286087;390.348364;0;1;87;0;10;2;1.44569147;0;73.5846309;14.3550552;0;0;0;0 +87.5;0;0;1;10;1;2;1;1;0;1429.79771;2253.5608;0.936027304;337.421302;83.4302077;386.080877;812.789748;313.802579;203.187576;382.017125;0;14302.8104;0;73.8630811;20.5175225;0.157451977;391.855816;0;1;87.5;0;10;2;1.4572789;0;73.8687488;14.3028104;0;0;0;0 +88;0;0;1;10;1;2;1;1;0;1429.79641;2253.61048;0.936044774;337.428431;83.4316886;387.022847;810.828608;313.809197;202.697404;382.968899;0;14251.211;0;74.1457358;20.5960377;0.156626785;393.355345;0;1;88;0;10;2;1.46886653;0;74.1513738;14.251211;0;0;0;0 +88.5;0;0;1;10;1;2;1;1;0;1429.79512;2253.65936;0.936061965;337.435447;83.4331459;387.958455;808.889986;313.81571;202.21286;383.914198;0;14200.2444;0;74.4269132;20.6741426;0.155810344;394.847038;0;1;88.5;0;10;2;1.48045437;0;74.4325218;14.2002444;0;0;0;0 +89;0;0;1;10;1;2;1;1;0;1429.79386;2253.70747;0.936078886;337.442352;83.4345802;388.887782;806.973463;313.82212;201.733839;384.853105;0;14149.8983;0;74.7066289;20.7518414;0.155002486;396.330976;0;1;89;0;10;2;1.49204241;0;74.7122085;14.1498983;0;0;0;0 +89.5;0;0;1;10;1;2;1;1;0;1429.79261;2253.75483;0.936095541;337.449149;83.435992;389.810905;805.078631;313.82843;201.260238;385.7857;0;14100.1608;0;74.9848983;20.8291384;0.154203051;397.807241;0;1;89.5;0;10;2;1.50363064;0;74.990449;14.1001608;0;0;0;0 +90;0;0;1;10;1;2;1;1;0;1429.79139;2253.80146;0.936111937;337.455841;83.4373819;390.727902;803.205094;313.834642;200.79196;386.712063;0;14051.0203;0;75.2617363;20.9060378;0.153411883;399.275912;0;1;90;0;10;2;1.51521907;0;75.2672585;14.0510203;0;0;0;0 +90.5;0;0;1;10;1;2;1;1;0;1429.79018;2253.84736;0.936128081;337.462429;83.4387504;391.63885;801.352465;313.840758;200.328906;387.632272;0;14002.4656;0;75.5371576;20.9825438;0.15262883;400.737067;0;1;90.5;0;10;2;1.5268077;0;75.5426517;14.0024656;0;0;0;0 +91;0;0;1;10;1;2;1;1;0;1429.78899;2253.89256;0.936143977;337.468916;83.4400978;392.543822;799.520366;313.84678;199.870983;388.546403;0;13954.4856;0;75.8111768;21.0586602;0.151853742;402.190784;0;1;91;0;10;2;1.53839651;0;75.816643;13.9544856;0;0;0;0 +91.5;0;0;1;10;1;2;1;1;0;1429.78782;2253.93708;0.936159632;337.475305;83.4414248;393.442892;797.70843;313.852711;199.418099;389.45453;0;13907.0696;0;76.083808;21.1343911;0.151086474;403.637138;0;1;91.5;0;10;2;1.5499855;0;76.0892466;13.9070696;0;0;0;0 +92;0;0;1;10;1;2;1;1;0;1429.78666;2253.98092;0.93617505;337.481597;83.4427318;394.33613;795.916299;313.858553;198.970164;390.356727;0;13860.2072;0;76.3550652;21.2097403;0.150326888;405.076202;0;1;92;0;10;2;1.56157468;0;76.3604764;13.8602072;0;0;0;0 +92.5;0;0;1;10;1;2;1;1;0;1429.78553;2254.02411;0.936190238;337.487796;83.4440192;395.223607;794.143624;313.864307;198.527092;391.253065;0;13813.8882;0;76.6249621;21.2847117;0.149574843;406.508049;0;1;92.5;0;10;2;1.57316404;0;76.6303463;13.8138882;0;0;0;0 +93;0;0;1;10;1;2;1;1;0;1429.78441;2254.06666;0.9362052;337.493902;83.4452875;396.105391;792.390063;313.869976;198.088796;392.143616;0;13768.1027;0;76.8935122;21.3593089;0.148830208;407.932752;0;1;93;0;10;2;1.58475358;0;76.8988695;13.7681027;0;0;0;0 +93.5;0;0;1;10;1;2;1;1;0;1429.78331;2254.10857;0.936219941;337.499917;83.446537;396.981551;790.655286;313.875562;197.655195;393.028447;0;13722.841;0;77.1607286;21.4335357;0.148092852;409.350379;0;1;93.5;0;10;2;1.59634329;0;77.1660594;13.722841;0;0;0;0 +94;0;0;1;10;1;2;1;1;0;1429.78222;2254.14988;0.936234466;337.505845;83.4477682;397.852151;788.938968;313.881065;197.226207;393.907627;0;13678.0938;0;77.4266243;21.5073956;0.147362647;410.760999;0;1;94;0;10;2;1.60793318;0;77.4319288;13.6780938;0;0;0;0 +94.5;0;0;1;10;1;2;1;1;0;1429.78115;2254.19058;0.936248779;337.511687;83.4489815;398.717256;787.240791;313.886488;196.801753;394.781221;0;13633.8518;0;77.6912121;21.5808923;0.146639469;412.164682;0;1;94.5;0;10;2;1.61952323;0;77.6964906;13.6338518;0;0;0;0 +95;0;0;1;10;1;2;1;1;0;1429.78009;2254.23069;0.936262886;337.517444;83.4501773;399.576931;785.560449;313.891834;196.381756;395.649296;0;13590.106;0;77.9545046;21.6540291;0.145923198;413.561492;0;1;95;0;10;2;1.63111345;0;77.9597573;13.590106;0;0;0;0 +95.5;0;0;1;10;1;2;1;1;0;1429.77905;2254.27023;0.936276791;337.523118;83.4513559;400.431238;783.897639;313.897102;195.96614;396.511915;0;13546.8478;0;78.2165141;21.7268095;0.145213716;414.951495;0;1;95.5;0;10;2;1.64270384;0;78.2217412;13.5468478;0;0;0;0 +96;0;0;1;10;1;2;1;1;0;1429.77803;2254.30921;0.936290498;337.528712;83.4525178;401.280238;782.252068;313.902296;195.554833;397.369141;0;13504.0687;0;78.4772526;21.7992368;0.144510907;416.334756;0;1;96;0;10;2;1.65429439;0;78.4824545;13.5040687;0;0;0;0 +96.5;0;0;1;10;1;2;1;1;0;1429.77702;2254.34763;0.93630401;337.534226;83.4536632;402.12399;780.623448;313.907416;195.147762;398.221035;0;13461.7602;0;78.7367321;21.8713145;0.143814659;417.711337;0;1;96.5;0;10;2;1.6658851;0;78.7419089;13.4617602;0;0;0;0 +97;0;0;1;10;1;2;1;1;0;1429.77602;2254.38552;0.936317333;337.539664;83.4547925;402.962555;779.011499;313.912464;194.744857;399.067658;0;13419.9142;0;78.9949643;21.9430456;0.143124863;419.081301;0;1;97;0;10;2;1.67747596;0;79.0001163;13.4199142;0;0;0;0 +97.5;0;0;1;10;1;2;1;1;0;1429.77504;2254.42287;0.93633047;337.545025;83.455906;403.795991;777.415947;313.917442;194.34605;399.90907;0;13378.523;0;79.2519607;22.0144335;0.142441412;420.44471;0;1;97.5;0;10;2;1.68906699;0;79.2570881;13.378523;0;0;0;0 +98;0;0;1;10;1;2;1;1;0;1429.77407;2254.45971;0.936343425;337.550312;83.4570041;404.624353;775.836524;313.922351;193.951274;400.745327;0;13337.5786;0;79.5077326;22.0854813;0.1417642;421.801622;0;1;98;0;10;2;1.70065816;0;79.5128356;13.3375786;0;0;0;0 +98.5;0;0;1;10;1;2;1;1;0;1429.77311;2254.49605;0.936356202;337.555526;83.4580871;405.447698;774.272968;313.927193;193.560464;401.576489;0;13297.0736;0;79.7622912;22.156192;0.141093127;423.152097;0;1;98.5;0;10;2;1.71224949;0;79.7673701;13.2970736;0;0;0;0 +99;0;0;1;10;1;2;1;1;0;1429.77217;2254.53188;0.936368804;337.560669;83.4591553;406.266081;772.725026;313.931968;193.173556;402.40261;0;13257.0006;0;80.0156474;22.2265687;0.140428092;424.496194;0;1;99;0;10;2;1.72384097;0;80.0207024;13.2570006;0;0;0;0 +99.5;0;0;1;10;1;2;1;1;0;1429.77124;2254.56723;0.936381235;337.565742;83.460209;407.079556;771.192446;313.936679;192.790487;403.223747;0;13217.3523;0;80.2678121;22.2966145;0.139768999;425.833969;0;1;99.5;0;10;2;1.73543259;0;80.2728433;13.2173523;0;0;0;0 +100;0;0;1;10;1;2;1;1;0;1429.77032;2254.6021;0.936393498;337.570746;83.4612485;407.888176;769.674984;313.941326;192.411197;404.039952;0;13178.1218;0;80.5187957;22.3663321;0.139115752;427.165478;0;1;100;0;10;2;1.74702436;0;80.5238034;13.1781218;0;0;0;0 +100.5;0;0;0;0;1;2;0;1;0;1456.13478;-287.052331;0.00482596159;-43.771484;0.438072152;148.540507;0;0;0;0;0;0;0;80.4545914;22.3484976;-0.0392193156;426.824864;0;1;100.5;0;0;2;1.74914239;0;80.4531795;0;0;0;0;0 +101;0;0;0;0;1;2;0;1;0;1385.13489;-299.93044;2.48719212e-05;-43.5052063;0.00214764063;54.0939488;0;0;0;0;0;0;0;80.3840282;22.3288967;-0.0391849513;426.450514;0;1;101;0;0;2;1.74915319;0;80.3826176;0;0;0;0;0 +101.5;0;0;0;0;1;2;0;1;0;1313.51849;-299.99963;1.28184291e-07;-41.2653496;1.04961785e-05;19.699376;0;0;0;0;0;0;0;80.3135268;22.309313;-0.0391506472;426.076493;0;1;101.5;0;0;2;1.74915324;0;80.3121174;0;0;0;0;0 +102;0;0;0;0;1;2;0;1;0;1241.89879;-299.999998;6.6063302e-10;-39.0154008;5.11454129e-08;7.17391547;0;0;0;0;0;0;0;80.2430871;22.2897464;-0.0391164031;425.702799;0;1;102;0;0;2;1.74915324;0;80.2416789;0;0;0;0;0 +102.5;0;0;0;0;1;2;0;1;0;1170.27906;-300;3.40475408e-12;-36.7654011;2.48390736e-10;2.6125225;0;0;0;0;0;0;0;80.1727089;22.2701969;-0.039082219;425.329431;0;1;102.5;0;0;2;1.74915324;0;80.171302;0;0;0;0;0 +103;0;0;0;0;1;2;0;1;0;1098.65934;-300;1.75473372e-14;-34.5154011;1.20180632e-12;0.951401483;0;0;0;0;0;0;0;80.1023923;22.2506645;-0.0390480948;424.956389;0;1;103;0;0;2;1.74915324;0;80.1009866;0;0;0;0;0 +103.5;0;0;0;0;1;2;0;1;0;1027.03961;-300;9.04350313e-17;-32.2654011;5.79007378e-15;0.346471574;0;0;0;0;0;0;0;80.032137;22.2311492;-0.0390140302;424.583674;0;1;103.5;0;0;2;1.74915324;0;80.0307325;0;0;0;0;0 +104;0;0;0;0;1;2;0;1;0;955.41989;-300;4.66081822e-19;-30.0154011;2.77598204e-17;0.126174442;0;0;0;0;0;0;0;79.961943;22.2116508;-0.0389800251;424.211283;0;1;104;0;0;2;1.74915324;0;79.9605397;0;0;0;0;0 +104.5;0;0;0;0;1;2;0;1;0;883.800166;-300;2.40208094e-21;-27.7654011;1.32343289e-19;0.0459489062;0;0;0;0;0;0;0;79.8918101;22.1921695;-0.0389460795;423.839216;0;1;104.5;0;0;2;1.74915324;0;79.890408;0;0;0;0;0 +105;0;0;0;0;1;2;0;1;0;812.180441;-300;1.23797852e-23;-25.5154011;6.26795464e-22;0.0167331985;0;0;0;0;0;0;0;79.8217383;22.1727051;-0.0389121932;423.467474;0;1;105;0;0;2;1.74915324;0;79.8203374;0;0;0;0;0 +105.5;0;0;0;0;1;2;0;1;0;740.560717;-300;6.380263e-26;-23.2654011;2.94550294e-24;0.00609372354;0;0;0;0;0;0;0;79.7517274;22.1532576;-0.0388783661;423.096054;0;1;105.5;0;0;2;1.74915324;0;79.7503278;0;0;0;0;0 +106;0;0;0;0;1;2;0;1;0;668.940992;-300;3.28824413e-28;-21.0154011;1.37123558e-26;0.00221914935;0;0;0;0;0;0;0;79.6817773;22.133827;-0.0388445979;422.724958;0;1;106;0;0;2;1.74915324;0;79.6803789;0;0;0;0;0 +106.5;0;0;0;0;1;2;0;1;0;597.340088;-288.058501;0.00504215479;-18.019012;0.187757824;0.00080814691;0;0;0;0;0;0;0;79.611888;22.1144133;-0.0388108887;422.354183;0;1;106.5;0;0;2;1.74915393;0;79.6104908;0;0;0;0;0 +107;0;0;0;0;1;2;0;1;0;591.236354;-15.5880564;0.120877249;-0.965120889;4.45518668;0.000294302602;0;0;0;0;0;0;0;79.5420593;22.0950165;-0.0387772383;421.983731;0;1;107;0;0;2;1.74976207;0;79.5406633;0;0;0;0;0 +107.5;0;0;0;0;1;2;0;1;0;590.489875;-0.325144471;0.127467051;-0.0201056189;4.69213611;0.000107176084;0;0;0;0;0;0;0;79.4722911;22.0756364;-0.0387436465;421.613599;0;1;107.5;0;0;2;1.7504083;0;79.4708964;0;0;0;0;0 +108;0;0;0;0;1;2;0;1;0;590.431974;0.0467028363;0.127633225;0.00288763131;4.69779239;3.90302803e-05;0;0;0;0;0;0;0;79.4025833;22.0562731;-0.0387101133;421.243788;0;1;108;0;0;2;1.75106016;0;79.4011898;0;0;0;0;0 +108.5;0;0;0;0;1;2;0;1;0;590.428998;0.00756366785;0.127616988;0.000467658407;4.69717106;1.42136447e-05;0;0;0;0;0;0;0;79.3329359;22.0369266;-0.0386766385;420.874296;0;1;108.5;0;0;2;1.7517125;0;79.3315435;0;0;0;0;0 +109;0;0;0;0;1;2;0;1;0;590.428954;0.00067157162;0.127614062;4.15229881e-05;4.69706303;5.17617844e-06;0;0;0;0;0;0;0;79.2633486;22.0175968;-0.0386432219;420.505125;0;1;109;0;0;2;1.75236487;0;79.2619575;0;0;0;0;0 +109.5;0;0;0;0;1;2;0;1;0;590.428965;4.05975853e-05;0.127613792;2.51013151e-06;4.69705318;1.88500725e-06;0;0;0;0;0;0;0;79.1938214;21.9982837;-0.0386098635;420.136271;0;1;109.5;0;0;2;1.75301724;0;79.1924315;0;0;0;0;0 +110;0;0;0;0;1;2;0;1;0;590.428967;1.2604612e-06;0.127613775;7.79337828e-08;4.69705257;6.86462486e-07;0;0;0;0;0;0;0;79.1243543;21.9789873;-0.038576563;419.767737;0;1;110;0;0;2;1.75366961;0;79.1229655;0;0;0;0;0 +110.5;0;0;0;0;1;2;0;1;0;590.428967;-7.08418497e-08;0.127613774;-4.38012159e-09;4.69705255;2.49988824e-07;0;0;0;0;0;0;0;79.054947;21.9597075;-0.0385433205;419.39952;0;1;110.5;0;0;2;1.75432197;0;79.0535594;0;0;0;0;0 +111;0;0;0;0;1;2;0;1;0;590.428967;-1.57460249e-08;0.127613774;-9.73570058e-10;4.69705255;9.10383501e-08;0;0;0;0;0;0;0;78.9855995;21.9404443;-0.0385101358;419.03162;0;1;111;0;0;2;1.75497434;0;78.9842131;0;0;0;0;0 +111.5;0;0;0;0;1;2;0;1;0;590.428967;-1.54557256e-09;0.127613774;-9.55620973e-11;4.69705255;3.31534068e-08;0;0;0;0;0;0;0;78.9163116;21.9211977;-0.0384770087;418.664036;0;1;111.5;0;0;2;1.75562671;0;78.9149265;0;0;0;0;0 +112;0;0;0;0;1;2;0;1;0;590.428967;-1.01294972e-10;0.127613774;-6.26302528e-12;4.69705255;1.20734656e-08;0;0;0;0;0;0;0;78.8470834;21.9019676;-0.0384439391;418.296769;0;1;112;0;0;2;1.75627908;0;78.8456994;0;0;0;0;0 +112.5;0;0;0;0;1;2;0;1;0;590.428967;-3.18323146e-12;0.127613774;-1.96817854e-13;4.69705255;4.39679009e-09;0;0;0;0;0;0;0;78.7779146;21.8827541;-0.0384109269;417.929817;0;1;112.5;0;0;2;1.75693145;0;78.7765319;0;0;0;0;0 +113;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;1.60117764e-09;0;0;0;0;0;0;0;78.7088052;21.863557;-0.038377972;417.56318;0;1;113;0;0;2;1.75758382;0;78.7074236;0;0;0;0;0 +113.5;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;5.83100349e-10;0;0;0;0;0;0;0;78.6397551;21.8443764;-0.0383450742;417.196858;0;1;113.5;0;0;2;1.75823618;0;78.6383747;0;0;0;0;0 +114;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;2.12347468e-10;0;0;0;0;0;0;0;78.5707641;21.8252122;-0.0383122335;416.83085;0;1;114;0;0;2;1.75888855;0;78.5693849;0;0;0;0;0 +114.5;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;7.73305093e-11;0;0;0;0;0;0;0;78.5018322;21.8060645;-0.0382794497;416.465154;0;1;114.5;0;0;2;1.75954092;0;78.5004541;0;0;0;0;0 +115;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;2.81614268e-11;0;0;0;0;0;0;0;78.4329592;21.7869331;-0.0382467226;416.099772;0;1;115;0;0;2;1.76019329;0;78.4315824;0;0;0;0;0 +115.5;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;1.02555378e-11;0;0;0;0;0;0;0;78.3641451;21.7678181;-0.0382140522;415.734702;0;1;115.5;0;0;2;1.76084566;0;78.3627694;0;0;0;0;0 +116;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;3.73475592e-12;0;0;0;0;0;0;0;78.2953898;21.7487194;-0.0381814383;415.369943;0;1;116;0;0;2;1.76149803;0;78.2940153;0;0;0;0;0 +116.5;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;1.36008487e-12;0;0;0;0;0;0;0;78.2266931;21.729637;-0.0381488809;415.005496;0;1;116.5;0;0;2;1.76215039;0;78.2253197;0;0;0;0;0 +117;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;4.95301672e-13;0;0;0;0;0;0;0;78.1580549;21.7105708;-0.0381163797;414.64136;0;1;117;0;0;2;1.76280276;0;78.1566828;0;0;0;0;0 +117.5;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;1.80373851e-13;0;0;0;0;0;0;0;78.0894753;21.6915209;-0.0380839347;414.277533;0;1;117.5;0;0;2;1.76345513;0;78.0881043;0;0;0;0;0 +118;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;6.56866877e-14;0;0;0;0;0;0;0;78.0209539;21.6724872;-0.0380515458;413.914016;0;1;118;0;0;2;1.7641075;0;78.0195841;0;0;0;0;0 +118.5;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;2.39211001e-14;0;0;0;0;0;0;0;77.9524908;21.6534697;-0.0380192128;413.550808;0;1;118.5;0;0;2;1.76475987;0;77.9511221;0;0;0;0;0 +119;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;8.71133935e-15;0;0;0;0;0;0;0;77.8840859;21.6344683;-0.0379869356;413.187909;0;1;119;0;0;2;1.76541224;0;77.8827184;0;0;0;0;0 +119.5;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;3.17240567e-15;0;0;0;0;0;0;0;77.815739;21.615483;-0.037954714;412.825317;0;1;119.5;0;0;2;1.7660646;0;77.8143726;0;0;0;0;0 +120;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;1.15529396e-15;0;0;0;0;0;0;0;77.74745;21.5965139;-0.0379225481;412.463033;0;1;120;0;0;2;1.76671697;0;77.7460848;0;0;0;0;0 +120.5;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;4.2072303e-16;0;0;0;0;0;0;0;77.6792189;21.5775608;-0.0378904376;412.101056;0;1;120.5;0;0;2;1.76736934;0;77.6778549;0;0;0;0;0 +121;0;0;0;0;1;2;0;1;0;590.428967;-6.30961949e-12;0.127613774;-3.90121103e-13;4.69705255;1.53214571e-16;0;0;0;0;0;0;0;77.6110456;21.5586238;-0.0378583825;411.739385;0;1;121;0;0;2;1.76802171;0;77.6096827;0;0;0;0;0 diff --git a/tools/vehicle_graph/tests/diesel-electric.script b/tools/vehicle_graph/tests/diesel-electric.script new file mode 100644 index 00000000..88647b6f --- /dev/null +++ b/tools/vehicle_graph/tests/diesel-electric.script @@ -0,0 +1,12 @@ +# events for: -graph-run diesel-electric.vhg -script diesel-electric.script +# options: -every 50 +# start the engine and wait at idle, then raise the controller in steps (engine speed and excitation) and back to zero +# (the generator voltage falls, the engine returns to idle) +1.0 press starter +1.5 release starter +10.0 value throttle 0.1 +15.0 value throttle 0.3 +25.0 value throttle 0.6 +40.0 value throttle 1 +100.0 value throttle 0 +120.0 value throttle 0 diff --git a/tools/vehicle_graph/tests/diesel-electric.vhg b/tools/vehicle_graph/tests/diesel-electric.vhg new file mode 100644 index 00000000..692779c1 --- /dev/null +++ b/tools/vehicle_graph/tests/diesel-electric.vhg @@ -0,0 +1,37 @@ +// expect: ok +// diesel-electric locomotive: engine, main generator, four series dc traction motors in parallel. the controller sets +// both the engine speed and the excitation of the generator; the motors are connected from its first position. +// start the engine, raise the controller step by step: the generator voltage rises with the excitation and the speed +{ + "format": "maszyna-vehicle-graph", + "version": 2, + "vehicle": { "name": "diesel electric test" }, + "body": { "mass": 72000, "length": 14.24, "wheel_diameter": 1.0, "axle_arrangement": "Bo'Bo'", "power": 588, "vmax": 90, "bearing": "roll" }, + "nodes": [ + { "id": "starter", "type": "cab.switch", "params": { "behaviour": "monostable" } }, + { "id": "throttle", "type": "cab.switch", "params": { "behaviour": "multi", "positions": 11, "min": 0, "max": 1, "default": 0 } }, + { "id": "reverser", "type": "cab.switch", "params": { "behaviour": "multi", "positions": 3, "min": -1, "max": 1, "default": 1 } }, + { "id": "power", "type": "logic.compare", "params": { "op": "gt", "value": 0 } }, + { "id": "engine", "type": "drive.diesel", "params": { "idle_rpm": 600, "max_rpm": 1500 } }, + { "id": "gen", "type": "drive.generator", "params": { "u_max": 750, "i_max": 2400, "p_max": 330, "nominal_rpm": 1500, "eff": 0.93, "field_time": 0.5, "inertia": 20 } }, + { "id": "motors", "type": "drive.dc_series", "params": { + "winding_res": 0.05, "circuit_res": 0.02, "volt": 750, "imax_lo": 900, "imax_hi": 900, "trip_delay": 1.0, + "mpt": [[0, 3.6, 200, 0.0, 22.6, 200, 0.0]], + "rlist": [[0, 0, 0, 0, 0], [1, 0, 4, 1, 0]] } }, + { "id": "final", "type": "drive.gear", "params": { "ratio": 5.0, "eff": 0.97 } }, + { "id": "axles", "type": "drive.axle", "params": { "powered_axles": 4 } }, + { "id": "vs", "type": "veh.state" } + ], + "connections": [ + { "from": "starter.pos", "to": "engine.start" }, + { "from": "throttle.pos", "to": "engine.demand" }, + { "from": "throttle.pos", "to": "gen.excitation" }, + { "from": "throttle.pos", "to": "power.a" }, + { "from": "power.q", "to": "motors.position" }, + { "from": "reverser.pos", "to": "motors.dir" }, + { "from": "engine.shaft", "to": "gen.in" }, + { "from": "gen.out", "to": "motors.wn" }, + { "from": "motors.shaft", "to": "final.in" }, + { "from": "final.out", "to": "axles.shaft" } + ] +} diff --git a/tools/vehicle_graph/tests/diesel-hydro.expected.csv b/tools/vehicle_graph/tests/diesel-hydro.expected.csv new file mode 100644 index 00000000..1f0b73bf --- /dev/null +++ b/tools/vehicle_graph/tests/diesel-hydro.expected.csv @@ -0,0 +1,243 @@ +time;starter.pos;starter.index;stop.pos;stop.index;throttle.pos;throttle.index;reverser.pos;reverser.index;power.q;engine.running;engine.starting;engine.rpm;engine.torque;engine.fill;engine.power;engine.fuel_rate;tc.fill;tc.lockup;tc.ratio;axles.force;axles.slip;vs.v;vs.v_signed;vs.accel;vs.wheel_rpm;vs.slipping;vs.cab;vs.time;starter:position;stop:position;throttle:position;reverser:position;engine:fuel_used;body:v;body:ft;body:fb;body:pg;body:zg;body:bc +0.5;0;0;0;0;0;0;1;2;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0.5;0;0;0;2;0;0;0;0;0;0;0 +1;0;0;0;0;0;0;1;2;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;0;0;2;0;0;0;0;0;0;0 +1.5;1;1;0;0;0;0;1;2;0;0;1;94.1596943;93.9309908;0;-2.95811404;0;0;0;0;0;0;0;0;0;0;0;1;1.5;1;0;0;2;0;0;0;0;0;0;0 +2;0;0;0;0;0;0;1;2;0;0;1;127.576789;51.1318276;0;-4.00794303;0;0;0;0;0;0;0;0;0;0;0;1;2;0;0;0;2;0;0;0;0;0;0;0 +2.5;0;0;0;0;0;0;1;2;0;0;1;146.672895;30.6059411;0;-4.6078649;0;0;0;0;0;0;0;0;0;0;0;1;2.5;0;0;0;2;0;0;0;0;0;0;0 +3;0;0;0;0;0;0;1;2;0;1;1;158.368911;97.5109825;0.1;-3.67620849;0.987256811;0;0;0;0;0;0;0;0;0;0;1;3;0;0;0;2;2.74238003e-06;0;0;0;0;0;0 +3.5;0;0;0;0;0;0;1;2;0;1;0;677.707926;503.756274;0.314683506;35.7512846;13.2946453;0;0;0;0;0;0;0;0;0;0;1;3.5;0;0;0;2;0.00241944164;0;0;0;0;0;0 +4;0;0;0;0;0;0;1;2;0;1;0;611.528056;-296.102004;0.00162180805;-18.9620965;0.0618266973;0;0;0;0;0;0;0;0;0;0;1;4;0;0;0;2;0.00275627066;0;0;0;0;0;0 +4.5;0;0;0;0;0;0;1;2;0;1;0;589.194397;-8.88795853;0.123999137;-0.548389645;4.55446617;0;0;0;0;0;0;0;0;0;0;1;4.5;0;0;0;2;0.0033188495;0;0;0;0;0;0 +5;0;0;0;0;0;0;1;2;0;1;0;590.418379;2.1455467;0.12852792;0.132655866;4.73061449;0;0;0;0;0;0;0;0;0;0;1;5;0;0;0;2;0.00397720921;0;0;0;0;0;0 +5.5;0;0;0;0;0;0;1;2;0;1;0;590.437332;-0.135942722;0.127554791;-0.00840540007;4.69494807;0;0;0;0;0;0;0;0;0;0;1;5.5;0;0;0;2;0.00462957007;0;0;0;0;0;0 +6;0;0;0;0;0;0;1;2;0;1;0;590.428291;-0.000897745739;0.127613486;-5.55071757e-05;4.69703656;0;0;0;0;0;0;0;0;0;0;1;6;0;0;0;2;0.00528190307;0;0;0;0;0;0 +6.5;0;0;0;0;0;0;1;2;0;1;0;590.428976;0.000897415629;0.127614155;5.54868295e-05;4.69706663;0;0;0;0;0;0;0;0;0;0;1;6.5;0;0;0;2;0.00593427465;0;0;0;0;0;0 +7;0;0;0;0;0;0;1;2;0;1;0;590.42897;-7.39056895e-05;0.127613743;-4.56955753e-06;4.6970514;0;0;0;0;0;0;0;0;0;0;1;7;0;0;0;2;0.006586643;0;0;0;0;0;0 +7.5;0;0;0;0;0;0;1;2;0;1;0;590.428967;1.13081643e-06;0.127613775;6.99179012e-08;4.69705257;0;0;0;0;0;0;0;0;0;0;1;7.5;0;0;0;2;0.00723901139;0;0;0;0;0;0 +8;0;0;0;0;0;0;1;2;0;1;0;590.428967;3.44821217e-07;0.127613775;2.13201499e-08;4.69705256;0;0;0;0;0;0;0;0;0;0;1;8;0;0;0;2;0.00789137981;0;0;0;0;0;0 +8.5;0;0;0;0;0;0;1;2;0;1;0;590.428967;-3.72784825e-08;0.127613774;-2.3049128e-09;4.69705255;0;0;0;0;0;0;0;0;0;0;1;8.5;0;0;0;2;0.00854374822;0;0;0;0;0;0 +9;0;0;0;0;0;0;1;2;0;1;0;590.428967;1.21775656e-09;0.127613774;7.52933729e-11;4.69705255;0;0;0;0;0;0;0;0;0;0;1;9;0;0;0;2;0.00919611663;0;0;0;0;0;0 +9.5;0;0;0;0;0;0;1;2;0;1;0;590.428967;1.1522161e-10;0.127613774;7.12410339e-12;4.69705255;0;0;0;0;0;0;0;0;0;0;1;9.5;0;0;0;2;0.00984848503;0;0;0;0;0;0 +10;0;0;0;0;0;0;1;2;0;1;0;590.428967;-1.68824954e-11;0.127613774;-1.04383755e-12;4.69705255;0;0;0;0;0;0;0;0;0;0;1;10;0;0;0;2;0.0105008534;0;0;0;0;0;0 +10.5;0;0;0;0;0.3;3;1;2;1;1;0;918.837984;-192.864138;0.0367161304;-18.5574816;2.10308126;0.25;0;1.99932368;3683.96285;0;0.0170832246;0.00474534015;0.0307749944;0.0906293211;0;1;10.5;0;0;3;2;0.0132653653;0.0182131746;3.68396285;0;0;0;0 +11;0;0;0;0;0.3;3;1;2;1;1;0;847.627197;406.346522;0.248646156;36.0686633;13.138532;0.5;0;1.99462856;6255.41384;0;0.109412393;0.0303923315;0.0744922612;0.580450775;0;1;11;0;0;3;2;0.0145733144;0.112123916;6.25541384;0;0;0;0 +11.5;0;0;0;0;0.3;3;1;2;1;1;0;843.816858;657.096013;0.33748983;58.0638258;17.7528914;0.75;0;1.98552459;9256.50781;0;0.285662757;0.0793507658;0.119978284;1.51548799;0;1;11.5;0;0;3;2;0.0167776442;0.290014015;9.25650781;0;0;0;0 +12;0;0;0;0;0.3;3;1;2;1;1;0;838.060956;866.198264;0.412307123;76.018892;21.5405434;1;0;1.97210208;12091.9085;0;0.541477449;0.150410403;0.163061336;2.87262709;0;1;12;0;0;3;2;0.0195104101;0.547377895;12.0919085;0;0;0;0 +12.5;0;0;0;0;0.3;3;1;2;1;1;0;838.775965;879.906139;0.417015639;77.2877996;21.8051224;1;0;1.95665108;12017.6509;0;0.835666762;0.232129656;0.162798743;4.43334987;0;1;12.5;0;0;3;2;0.0225410619;0.841526591;12.0176509;0;0;0;0 +13;0;0;0;0;0.3;3;1;2;1;1;0;838.735893;879.329389;0.416819509;77.23345;21.7938258;1;0;1.9414154;11922.9348;0;1.12737526;0.313159794;0.161362087;5.98091151;0;1;13;0;0;3;2;0.0255678408;1.13318327;11.9229348;0;0;0;0 +13.5;0;0;0;0;0.3;3;1;2;1;1;0;838.738086;879.35258;0.416827284;77.2356889;21.7942893;1;0;1.92643555;11831.0001;0;1.41654054;0.393483484;0.159963955;7.51498097;0;1;13.5;0;0;3;2;0.0285948341;1.42229824;11.8310001;0;0;0;0 +14;0;0;0;0;0.3;3;1;2;1;1;0;838.737969;879.351709;0.416826998;77.2356015;21.7942713;1;0;1.91170354;11740.5217;0;1.70320877;0.473113547;0.158587589;9.03580316;0;1;14;0;0;3;2;0.0316218157;1.70891694;11.7405217;0;0;0;0 +14.5;0;0;0;0;0.3;3;1;2;1;1;0;838.737975;879.351738;0.416827007;77.2356047;21.794272;1;0;1.89721443;11651.5385;0;1.98742004;0.552061123;0.157233453;10.5435908;0;1;14.5;0;0;3;2;0.0346487979;1.99307948;11.6515385;0;0;0;0 +15;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.88296319;11564.0161;0;2.26921388;0.630337188;0.155901039;12.0385535;0;1;15;0;0;3;2;0.0376757802;2.27482536;11.5640161;0;0;0;0 +15.5;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.86894496;11477.9246;0;2.54862897;0.707952492;0.154589908;13.5208966;0;1;15.5;0;0;3;2;0.0407027624;2.55419327;11.4779246;0;0;0;0 +16;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.85515498;11393.2349;0;2.82570324;0.784917565;0.153299629;14.9908213;0;1;16;0;0;3;2;0.0437297446;2.8312211;11.3932349;0;0;0;0 +16.5;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.84158864;11309.9187;0;3.10047382;0.861242726;0.152029781;16.4485245;0;1;16.5;0;0;3;2;0.0467567268;3.10594598;11.3099187;0;0;0;0 +17;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.82824142;11227.9482;0;3.37297711;0.936938086;0.150779955;17.8941993;0;1;17;0;0;3;2;0.049783709;3.3784043;11.2279482;0;0;0;0 +17.5;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.81510894;11147.2965;0;3.64324879;1.01201355;0.149549752;19.3280351;0;1;17.5;0;0;3;2;0.0528106912;3.6486317;11.1472965;0;0;0;0 +18;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.80218691;11067.9372;0;3.91132382;1.08647884;0.148338783;20.7502173;0;1;18;0;0;3;2;0.0558376735;3.91666315;11.0679372;0;0;0;0 +18.5;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.78947116;10989.8448;0;4.17723647;1.16034346;0.147146668;22.1609277;0;1;18.5;0;0;3;2;0.0588646557;4.1825329;10.9898448;0;0;0;0 +19;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.77695763;10912.9943;0;4.44102033;1.23361676;0.145973038;23.5603446;0;1;19;0;0;3;2;0.0618916379;4.44627453;10.9129943;0;0;0;0 +19.5;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.76464235;10837.3613;0;4.70270836;1.30630788;0.144817532;24.9486427;0;1;19.5;0;0;3;2;0.0649186201;4.70792096;10.8373613;0;0;0;0 +20;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.75252144;10762.9221;0;4.96233283;1.37842579;0.143679798;26.3259933;0;1;20;0;0;3;2;0.0679456023;4.96750449;10.7629221;0;0;0;0 +20.5;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.74059114;10689.6534;0;5.21992544;1.44997929;0.142559493;27.6925646;0;1;20.5;0;0;3;2;0.0709725845;5.22505678;10.6896534;0;0;0;0 +21;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.72884777;10617.5328;0;5.47551724;1.52097701;0.141456283;29.0485212;0;1;21;0;0;3;2;0.0739995667;5.48060888;10.6175328;0;0;0;0 +21.5;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.71728773;10546.5381;0;5.72913871;1.59142742;0.14036984;30.3940248;0;1;21.5;0;0;3;2;0.077026549;5.73419125;10.5465381;0;0;0;0 +22;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.70590754;10476.6478;0;5.98081972;1.66133881;0.139299848;31.7292341;0;1;22;0;0;3;2;0.0800535312;5.98583375;10.4766478;0;0;0;0 +22.5;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.69470376;10407.841;0;6.23058962;1.73071934;0.138245994;33.0543045;0;1;22.5;0;0;3;2;0.0830805134;6.23556572;10.407841;0;0;0;0 +23;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.68367307;10340.0972;0;6.47847715;1.79957699;0.137207975;34.3693888;0;1;23;0;0;3;2;0.0861074956;6.4834159;10.3400972;0;0;0;0 +23.5;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.67281221;10273.3964;0;6.72451056;1.8679196;0.136185496;35.6746365;0;1;23.5;0;0;3;2;0.0891344778;6.72941251;10.2733964;0;0;0;0 +24;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.662118;10207.7191;0;6.96871756;1.93575488;0.135178267;36.9701949;0;1;24;0;0;3;2;0.09216146;6.97358325;10.2077191;0;0;0;0 +24.5;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.65158735;10143.0462;0;7.21112532;2.00309037;0.134186005;38.256208;0;1;24.5;0;0;3;2;0.0951884422;7.2159553;10.1430462;0;0;0;0 +25;0;0;0;0;0.3;3;1;2;1;1;0;838.737974;879.351737;0.416827007;77.2356046;21.7942719;1;0;1.64121723;10079.3592;0;7.45176053;2.06993348;0.133208437;39.5328175;0;1;25;0;0;3;2;0.0982154245;7.45655534;10.0793592;0;0;0;0 +25.5;0;0;0;0;1;10;1;2;1;1;0;1273.75525;2615.73301;0.996994457;348.905662;79.1660434;1;0;1.74273113;24684.0607;0;7.870989;2.18638583;0.351256567;41.7568936;0;1;25.5;0;0;10;2;0.106548068;7.88376285;24.6840607;0;0;0;0 +26;0;0;0;0;1;10;1;2;1;1;0;1392.53214;2486.01921;0.99998451;362.525305;86.8077921;1;0;1.74307553;29508.0769;0;8.5976929;2.38824803;0.427691359;45.6121775;0;1;26;0;0;10;2;0.118271266;8.6131012;29.5080769;0;0;0;0 +26.5;0;0;0;0;1;10;1;2;1;1;0;1403.91288;2469.09219;0.99999992;362.999545;87.5185939;1;0;1.7237554;29659.9353;0;9.37272345;2.60353429;0.430344851;49.7238422;0;1;26.5;0;0;10;2;0.130396619;9.3882132;29.6599353;0;0;0;0 +27;0;0;0;0;1;10;1;2;1;1;0;1404.89048;2467.60449;1;363.033447;87.5795436;1;0;1.70310207;29345.3881;0;10.1431669;2.81754637;0.425556732;53.8111719;0;1;27;0;0;10;2;0.14255788;10.1584832;29.3453881;0;0;0;0 +27.5;0;0;0;0;1;10;1;2;1;1;0;1404.97366;2467.47767;1;363.036282;87.5847292;1;0;1.68283741;28999.6506;0;10.9043066;3.02897404;0.42026094;57.849143;0;1;27.5;0;0;10;2;0.154722208;10.9194322;28.9996506;0;0;0;0 +28;0;0;0;0;1;10;1;2;1;1;0;1404.98073;2467.46688;1;363.036523;87.58517;1;0;1.66310195;28659.846;0;11.6559735;3.23777042;0.415049784;61.83686;0;1;28;0;0;10;2;0.166886796;11.6709116;28.659846;0;0;0;0 +28.5;0;0;0;0;1;10;1;2;1;1;0;1404.98133;2467.46597;1;363.036543;87.5852075;1;0;1.64389265;28328.8409;0;12.3983794;3.44399428;0.409969317;65.7754457;0;1;28.5;0;0;10;2;0.179051407;12.4131347;28.3288409;0;0;0;0 +29;0;0;0;0;1;10;1;2;1;1;0;1404.98138;2467.46589;1;363.036545;87.5852107;1;0;1.62519413;28006.6155;0;13.131761;3.64771138;0.405019548;69.6661557;0;1;29;0;0;10;2;0.191216019;13.1463382;28.0066155;0;0;0;0 +29.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.60699055;27692.9173;0;13.8563501;3.84898614;0.400196756;73.5102203;0;1;29.5;0;0;10;2;0.203380632;13.8707537;27.6929173;0;0;0;0 +30;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.58926656;27387.4838;0;14.5723718;4.04788104;0.395497054;77.3088333;0;1;30;0;0;10;2;0.215545244;14.5866063;27.3874838;0;0;0;0 +30.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.57200747;27090.0615;0;15.2800441;4.24445668;0.390916683;81.0631514;0;1;30.5;0;0;10;2;0.227709857;15.2941138;27.0900615;0;0;0;0 +31;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.55519912;26800.4069;0;15.9795784;4.43877179;0.386452029;84.7742966;0;1;31;0;0;10;2;0.23987447;15.9934875;26.8004069;0;0;0;0 +31.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.53882794;26518.286;0;16.67118;4.63088334;0.382099621;88.4433569;0;1;31.5;0;0;10;2;0.252039082;16.6849325;26.518286;0;0;0;0 +32;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.52288088;26243.4738;0;17.3550477;4.82084659;0.377856123;92.0713877;0;1;32;0;0;10;2;0.264203695;17.3686475;26.2434738;0;0;0;0 +32.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.50734541;25975.7544;0;18.0313746;5.00871517;0.373718328;95.6594134;0;1;32.5;0;0;10;2;0.276368308;18.0448255;25.9757544;0;0;0;0 +33;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.49220946;25714.9199;0;18.7003482;5.19454116;0.36968315;99.2084282;0;1;33;0;0;10;2;0.28853292;18.7136539;25.7149199;0;0;0;0 +33.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.47746144;25460.7705;0;19.3621503;5.37837508;0.365747623;102.719398;0;1;33.5;0;0;10;2;0.300697533;19.3753144;25.4607705;0;0;0;0 +34;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.46309021;25213.1141;0;20.0169578;5.56026604;0.361908894;106.193259;0;1;34;0;0;10;2;0.312862146;20.0299837;25.2131141;0;0;0;0 +34.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.44908502;24971.7658;0;20.6649422;5.74026174;0.358164216;109.630924;0;1;34.5;0;0;10;2;0.325026758;20.6778335;24.9717658;0;0;0;0 +35;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.43543555;24736.5475;0;21.3062706;5.91840851;0.354510943;113.033276;0;1;35;0;0;10;2;0.337191371;21.3190304;24.7365475;0;0;0;0 +35.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.42213187;24507.2881;0;21.941105;6.0947514;0.350946531;116.401178;0;1;35.5;0;0;10;2;0.349355983;21.9537366;24.5072881;0;0;0;0 +36;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.4091644;24283.8226;0;22.5696032;6.26933423;0.347468529;119.735464;0;1;36;0;0;10;2;0.361520596;22.5821096;24.2838226;0;0;0;0 +36.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.39652393;24065.992;0;23.1919185;6.44219958;0.344074573;123.036949;0;1;36.5;0;0;10;2;0.373685209;23.2043028;24.065992;0;0;0;0 +37;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.38420157;23853.6435;0;23.8082;6.6133889;0.340762389;126.306424;0;1;37;0;0;10;2;0.385849821;23.8204651;23.8536435;0;0;0;0 +37.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.37218877;23646.6296;0;24.418593;6.78294251;0.337529783;129.54466;0;1;37.5;0;0;10;2;0.398014434;24.4307418;23.6466296;0;0;0;0 +38;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.36047729;23444.8082;0;25.0232388;6.95089966;0.334374642;132.752405;0;1;38;0;0;10;2;0.410179047;25.035274;23.4448082;0;0;0;0 +38.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.34905918;23248.0424;0;25.6222748;7.11729855;0.331294927;135.93039;0;1;38.5;0;0;10;2;0.422343659;25.6341992;23.2480424;0;0;0;0 +39;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.33792678;23056.2001;0;26.215835;7.2821764;0.328288674;139.079325;0;1;39;0;0;10;2;0.434508272;26.2276513;23.0562001;0;0;0;0 +39.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.32707269;22869.154;0;26.80405;7.44556945;0.325353985;142.199902;0;1;39.5;0;0;10;2;0.446672885;26.8157607;22.869154;0;0;0;0 +40;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.3164898;22686.7814;0;27.3870468;7.60751301;0.322489032;145.292796;0;1;40;0;0;10;2;0.458837497;27.3986544;22.6867814;0;0;0;0 +40.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.30617123;22508.9637;0;27.9649494;7.7680415;0.319692051;148.358664;0;1;40.5;0;0;10;2;0.47100211;27.9764563;22.5089637;0;0;0;0 +41;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.29611035;22335.5867;0;28.5378785;7.92718847;0.316961338;151.398148;0;1;41;0;0;10;2;0.483166723;28.5492872;22.3355867;0;0;0;0 +41.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.28630076;22166.5402;0;29.1059519;8.08498663;0.314295249;154.41187;0;1;41.5;0;0;10;2;0.495331335;29.1172646;22.1665402;0;0;0;0 +42;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.27673628;22001.7176;0;29.6692844;8.24146788;0.311692198;157.400442;0;1;42;0;0;10;2;0.507495948;29.6805034;22.0017176;0;0;0;0 +42.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.26741095;21841.0163;0;30.2279881;8.39666336;0.309150651;160.364457;0;1;42.5;0;0;10;2;0.51966056;30.2391157;21.8410163;0;0;0;0 +43;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.25831902;21684.337;0;30.7821723;8.55060343;0.306669129;163.304496;0;1;43;0;0;10;2;0.531825173;30.7932107;21.684337;0;0;0;0 +43.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.24945492;21531.5839;0;31.3319438;8.70331773;0.304246203;166.221125;0;1;43.5;0;0;10;2;0.543989786;31.342895;21.5315839;0;0;0;0 +44;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.24081329;21382.6646;0;31.8774068;8.85483522;0.301880491;169.114896;0;1;44;0;0;10;2;0.556154398;31.8882728;21.3826646;0;0;0;0 +44.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.23238895;21237.4897;0;32.418663;9.00518416;0.299570659;171.986349;0;1;44.5;0;0;10;2;0.568319011;32.4294459;21.2374897;0;0;0;0 +45;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.22417688;21095.9729;0;32.9558118;9.15439216;0.297315419;174.836012;0;1;45;0;0;10;2;0.580483624;32.9665135;21.0959729;0;0;0;0 +45.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.21617225;20958.0309;0;33.4889503;9.30248619;0.295113524;177.664399;0;1;45.5;0;0;10;2;0.592648236;33.4995728;20.9580309;0;0;0;0 +46;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.20837038;20823.583;0;34.0181734;9.44949261;0.292963769;180.472015;0;1;46;0;0;10;2;0.604812849;34.0287186;20.823583;0;0;0;0 +46.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.20076676;20692.5514;0;34.543574;9.59543721;0.290864992;183.259352;0;1;46.5;0;0;10;2;0.616977462;34.5540436;20.6925514;0;0;0;0 +47;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.19335702;20564.861;0;35.0652426;9.74034518;0.288816067;186.02689;0;1;47;0;0;10;2;0.629142074;35.0756386;20.564861;0;0;0;0 +47.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.18613694;20440.4389;0;35.5832682;9.88424116;0.286815906;188.775101;0;1;47.5;0;0;10;2;0.641306687;35.5935921;20.4404389;0;0;0;0 +48;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.17910244;20319.215;0;36.0977374;10.0271493;0.284863456;191.504445;0;1;48;0;0;10;2;0.653471299;36.1079911;20.319215;0;0;0;0 +48.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.17224959;20201.1212;0;36.6087352;10.1690931;0.282957701;194.215372;0;1;48.5;0;0;10;2;0.665635912;36.6189203;20.2011212;0;0;0;0 +49;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.16557456;20086.092;0;37.1163448;10.3100958;0.281097657;196.908325;0;1;49;0;0;10;2;0.677800525;37.126463;20.086092;0;0;0;0 +49.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.15907369;19974.0639;0;37.6206475;10.4501799;0.279282373;199.583734;0;1;49.5;0;0;10;2;0.689965137;37.6307003;19.9740639;0;0;0;0 +50;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.1527434;19864.9754;0;38.121723;10.5893675;0.277510927;202.242022;0;1;50;0;0;10;2;0.70212975;38.1317122;19.8649754;0;0;0;0 +50.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.14658025;19758.7672;0;38.6196496;10.7276804;0.275782431;204.883604;0;1;50.5;0;0;10;2;0.714294363;38.6295765;19.7587672;0;0;0;0 +51;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.14058092;19655.382;0;39.1145036;10.8651399;0.274096023;207.508886;0;1;51;0;0;10;2;0.726458975;39.1243698;19.655382;0;0;0;0 +51.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.13474218;19554.7643;0;39.6063601;11.0017667;0.27245087;210.118266;0;1;51.5;0;0;10;2;0.738623588;39.6161671;19.5547643;0;0;0;0 +52;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.12906092;19456.8604;0;40.0952925;11.1375812;0.270846167;212.712133;0;1;52;0;0;10;2;0.750788201;40.1050418;19.4568604;0;0;0;0 +52.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.12353414;19361.6185;0;40.581373;11.2726036;0.269281134;215.29087;0;1;52.5;0;0;10;2;0.762952813;40.591066;19.3616185;0;0;0;0 +53;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.11815891;19268.9885;0;41.0646723;11.4068534;0.267755018;217.854853;0;1;53;0;0;10;2;0.775117426;41.0743104;19.2689885;0;0;0;0 +53.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.11293244;19178.9218;0;41.5452597;11.5403499;0.266267089;220.404448;0;1;53.5;0;0;10;2;0.787282039;41.5548443;19.1789218;0;0;0;0 +54;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.107852;19091.3717;0;42.0232034;11.6731121;0.264816641;222.940018;0;1;54;0;0;10;2;0.799446651;42.0327358;19.0913717;0;0;0;0 +54.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.10291496;19006.2928;0;42.4985702;11.8051584;0.263402993;225.461917;0;1;54.5;0;0;10;2;0.811611264;42.5080517;19.0062928;0;0;0;0 +55;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.09811878;18923.6412;0;42.9714256;11.9365071;0.262025485;227.970493;0;1;55;0;0;10;2;0.823775876;42.9808576;18.9236412;0;0;0;0 +55.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.093461;18843.3748;0;43.4418343;12.0671762;0.260683477;230.466089;0;1;55.5;0;0;10;2;0.835940489;43.4512179;18.8433748;0;0;0;0 +56;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.08893925;18765.4524;0;43.9098594;12.1971832;0.259376354;232.949039;0;1;56;0;0;10;2;0.848105102;43.919196;18.7654524;0;0;0;0 +56.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.08455124;18689.8347;0;44.3755633;12.3265454;0.258103517;235.419675;0;1;56.5;0;0;10;2;0.860269714;44.3848541;18.6898347;0;0;0;0 +57;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.08029474;18616.4834;0;44.8390071;12.4552797;0.25686439;237.878321;0;1;57;0;0;10;2;0.872434327;44.8482533;18.6164834;0;0;0;0 +57.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.07616762;18545.3616;0;45.300251;12.583403;0.255658414;240.325295;0;1;57.5;0;0;10;2;0.88459894;45.3094538;18.5453616;0;0;0;0 +58;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.0721678;18476.4337;0;45.7593541;12.7109317;0.254485051;242.760913;0;1;58;0;0;10;2;0.896763552;45.7685147;18.4764337;0;0;0;0 +58.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.06829329;18409.6651;0;46.2163747;12.8378819;0.253343778;245.185483;0;1;58.5;0;0;10;2;0.908928165;46.2254943;18.4096651;0;0;0;0 +59;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.06454216;18345.0227;0;46.6713702;12.9642695;0.252234091;247.599309;0;1;59;0;0;10;2;0.921092778;46.6804498;18.3450227;0;0;0;0 +59.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.06091255;18282.4744;0;47.1243967;13.0901102;0.251155505;250.002689;0;1;59.5;0;0;10;2;0.93325739;47.1334376;18.2824744;0;0;0;0 +60;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.05740266;18221.9892;0;47.5755101;13.2154195;0.250107548;252.39592;0;1;60;0;0;10;2;0.945422003;47.5845132;18.2219892;0;0;0;0 +60.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.05401075;18163.5371;0;48.0247648;13.3402125;0.249089767;254.779291;0;1;60.5;0;0;10;2;0.957586616;48.0337314;18.1635371;0;0;0;0 +61;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.05073515;18107.0895;0;48.472215;13.4645042;0.248101723;257.153087;0;1;61;0;0;10;2;0.969751228;48.4811459;18.1070895;0;0;0;0 +61.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.04757425;18052.6184;0;48.9179136;13.5883093;0.247142994;259.517592;0;1;61.5;0;0;10;2;0.981915841;48.9268101;18.0526184;0;0;0;0 +62;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.0445265;18000.0971;0;49.3619131;13.7116425;0.246213169;261.873082;0;1;62;0;0;10;2;0.994080453;49.3707761;18.0000971;0;0;0;0 +62.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.04159039;17949.4999;0;49.8042652;13.8345181;0.245311857;264.219833;0;1;62.5;0;0;10;2;1.00624507;49.8130957;17.9494999;0;0;0;0 +63;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.03876449;17900.8018;0;50.2450207;13.9569502;0.244438677;266.558114;0;1;63;0;0;10;2;1.01840968;50.2538199;17.9008018;0;0;0;0 +63.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.03604741;17853.9789;0;50.6842301;14.0789528;0.243593264;268.888192;0;1;63.5;0;0;10;2;1.03057429;50.6929989;17.8539789;0;0;0;0 +64;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.03343782;17809.0083;0;51.121943;14.2005397;0.242775264;271.210331;0;1;64;0;0;10;2;1.0427389;51.1306823;17.8090083;0;0;0;0 +64.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.03093442;17765.8678;0;51.5582084;14.3217245;0.241984339;273.524791;0;1;64.5;0;0;10;2;1.05490352;51.5669192;17.7658678;0;0;0;0 +65;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.02853599;17724.5362;0;51.9930747;14.4425207;0.241220162;275.831828;0;1;65;0;0;10;2;1.06706813;52.0017581;17.7245362;0;0;0;0 +65.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.02624134;17684.993;0;52.4265898;14.5629416;0.24048242;278.131697;0;1;65.5;0;0;10;2;1.07923274;52.4352466;17.684993;0;0;0;0 +66;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.02404934;17647.2187;0;52.858801;14.6830003;0.239770809;280.424649;0;1;66;0;0;10;2;1.09139735;52.8674322;17.6472187;0;0;0;0 +66.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.0219589;17611.1946;0;53.289755;14.8027097;0.239085041;282.710931;0;1;66.5;0;0;10;2;1.10356197;53.2983616;17.6111946;0;0;0;0 +67;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.01996897;17576.9026;0;53.7194982;14.9220828;0.238424837;284.99079;0;1;67;0;0;10;2;1.11572658;53.7280811;17.5769026;0;0;0;0 +67.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.01807856;17544.3256;0;54.1480763;15.0411323;0.237789931;287.264467;0;1;67.5;0;0;10;2;1.12789119;54.1566363;17.5443256;0;0;0;0 +68;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.01628672;17513.4472;0;54.5755346;15.1598707;0.237180067;289.532204;0;1;68;0;0;10;2;1.14005581;54.5840727;17.5134472;0;0;0;0 +68.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.01459253;17484.2517;0;55.0019179;15.2783105;0.236595001;291.794237;0;1;68.5;0;0;10;2;1.15222042;55.0104349;17.4842517;0;0;0;0 +69;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.01299513;17456.724;0;55.4272707;15.3964641;0.236034499;294.050804;0;1;69;0;0;10;2;1.16438503;55.4357676;17.456724;0;0;0;0 +69.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.0114937;17430.8501;0;55.851637;15.5143436;0.235498338;296.302137;0;1;69.5;0;0;10;2;1.17654964;55.8601146;17.4308501;0;0;0;0 +70;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.01008744;17406.6164;0;56.2750604;15.6319612;0.234986306;298.548468;0;1;70;0;0;10;2;1.18871426;56.2835195;17.4066164;0;0;0;0 +70.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.00877562;17384.01;0;56.6975841;15.7493289;0.234498199;300.790025;0;1;70.5;0;0;10;2;1.20087887;56.7060257;17.38401;0;0;0;0 +71;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.00755753;17363.0189;0;57.119251;15.8664586;0.234033827;303.027038;0;1;71;0;0;10;2;1.21304348;57.1276759;17.3630189;0;0;0;0 +71.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.0064325;17343.6315;0;57.5401037;15.9833621;0.233593006;305.259731;0;1;71.5;0;0;10;2;1.22520809;57.5485127;17.3436315;0;0;0;0 +72;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.00539991;17325.8371;0;57.9601844;16.1000512;0.233175563;307.488328;0;1;72;0;0;10;2;1.23737271;57.9685784;17.3258371;0;0;0;0 +72.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.00445917;17309.6255;0;58.379535;16.2165375;0.232781336;309.713053;0;1;72.5;0;0;10;2;1.24953732;58.3879149;17.3096255;0;0;0;0 +73;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.00360972;17294.9872;0;58.7981973;16.3328326;0.232410171;311.934125;0;1;73;0;0;10;2;1.26170193;58.8065638;17.2949872;0;0;0;0 +73.5;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.00285106;17281.9134;0;59.2162124;16.4489479;0.232061923;314.151764;0;1;73.5;0;0;10;2;1.27386654;59.2245664;17.2819134;0;0;0;0 +74;0;0;0;0;1;10;1;2;1;1;0;1404.98139;2467.46588;1;363.036545;87.585211;1;0;1.0021827;17270.3957;0;59.6336217;16.5648949;0.231736457;316.366189;0;1;74;0;0;10;2;1.28603116;59.641964;17.2703957;0;0;0;0 +74.5;0;0;0;0;1;10;1;2;1;1;0;1402.20301;2471.68335;1;362.93792;87.4120096;1;1;1.00145505;17922.9883;0;60.0516014;16.6810004;0.240193543;318.58364;0;1;74.5;0;0;10;2;1.29819446;60.0602948;17.9229883;0;0;0;0 +75;0;0;0;0;1;10;1;2;1;1;0;1321.24667;2577.62478;1;356.641782;82.3652676;1;1;0.838038146;18742.9357;0;60.5113512;16.8087087;0.253717534;321.022689;0;1;75;0;0;10;2;1.30996316;60.5204866;18.7429357;0;0;0;0 +75.5;0;0;0;0;1;10;1;2;1;1;0;1270.82258;2627.04912;1;349.608298;79.2218775;1;1;0.560606207;19113.23;0;60.9726644;16.9368512;0.259096704;323.470031;0;1;75.5;0;0;10;2;1.32118202;60.981996;19.11323;0;0;0;0 +76;0;0;0;0;1;10;1;2;1;1;0;1215.73566;2666.51314;1;339.477897;75.7878115;1;1;0.234623908;19474.6853;0;61.4440427;17.0677896;0.264423188;325.97077;0;1;76;0;0;10;2;1.33194602;61.4535654;19.4746853;0;0;0;0 +76.5;0;0;0;0;1;10;1;2;1;1;0;1181.91151;2683.22626;1;332.101529;73.6792457;1;1;0;18739.6522;0;61.9120889;17.1978025;0.253201943;328.453833;0;1;76.5;0;0;10;2;1.34225321;61.9212038;18.7396522;0;0;0;0 +77;0;0;0;0;1;10;1;2;1;1;0;1190.60728;2679.4758;1;334.077318;74.2213315;1;1;0;18713.459;0;62.3673369;17.3242603;0.252633898;330.868999;0;1;77;0;0;10;2;1.35252485;62.3764313;18.713459;0;0;0;0 +77.5;0;0;0;0;1;10;1;2;1;1;0;1199.2829;2675.35727;1;335.994404;74.7621616;1;1;0;18684.6951;0;62.8215258;17.4504238;0.252025977;333.278546;0;1;77.5;0;0;10;2;1.36287169;62.8305983;18.6846951;0;0;0;0 +78;0;0;0;0;1;10;1;2;1;1;0;1207.93701;2670.87401;1;337.851854;75.3016506;1;1;0;18653.3841;0;63.2745842;17.5762734;0.251378572;335.682095;0;1;78;0;0;10;2;1.37329356;63.2836333;18.6533841;0;0;0;0 +78.5;0;0;0;0;1;10;1;2;1;1;0;1216.56826;2666.0296;1;339.648785;75.8397145;1;1;0;18619.5508;0;63.7264412;17.7017892;0.250692099;338.079271;0;1;78.5;0;0;10;2;1.38379025;63.7354656;18.6195508;0;0;0;0 +79;0;0;0;0;1;10;1;2;1;1;0;1225.17531;2660.82786;1;341.384369;76.3762699;1;1;0;18583.2217;0;64.1770271;17.826952;0.249966996;340.469703;0;1;79;0;0;10;2;1.39436157;64.1860254;18.5832217;0;0;0;0 +79.5;0;0;0;0;1;10;1;2;1;1;0;1233.75684;2655.27277;1;343.057827;76.9112345;1;1;0;18544.425;0;64.6262727;17.9517424;0.249203726;342.853025;0;1;79.5;0;0;10;2;1.40500729;64.6352435;18.544425;0;0;0;0 +80;0;0;0;0;1;10;1;2;1;1;0;1242.31155;2649.36856;1;344.66844;77.4445269;1;1;0;18503.19;0;65.0741098;18.0761416;0.248402772;345.228875;0;1;80;0;0;10;2;1.4157272;65.0830517;18.50319;0;0;0;0 +80.5;0;0;0;0;1;10;1;2;1;1;0;1250.83814;2643.11964;1;346.215538;77.9760669;1;1;0;18459.5475;0;65.5204709;18.2001308;0.24756464;347.596894;0;1;80.5;0;0;10;2;1.42652106;65.5293826;18.4595475;0;0;0;0 +81;0;0;0;0;1;10;1;2;1;1;0;1259.33536;2636.53061;1;347.698513;78.5057752;1;1;0;18413.5298;0;65.9652896;18.3236916;0.246689857;349.956731;0;1;81;0;0;10;2;1.43738861;65.9741698;18.4135298;0;0;0;0 +81.5;0;0;0;0;1;10;1;2;1;1;0;1267.80194;2629.60627;1;349.116807;79.033574;1;1;0;18365.1702;0;66.4085006;18.4468057;0.245778969;352.308038;0;1;81.5;0;0;10;2;1.4483296;66.4173479;18.3651702;0;0;0;0 +82;0;0;0;0;1;10;1;2;1;1;0;1276.23666;2622.3516;1;350.469924;79.5593863;1;1;0;18314.5036;0;66.8500392;18.5694553;0.244832542;354.650473;0;1;82;0;0;10;2;1.45934375;66.8588524;18.3145036;0;0;0;0 +82.5;0;0;0;0;1;10;1;2;1;1;0;1284.6383;2614.77175;1;351.75742;80.0831365;1;1;0;18261.5659;0;67.289842;18.6916228;0.243851161;356.983699;0;1;82.5;0;0;10;2;1.47043079;67.29862;18.2615659;0;0;0;0 +83;0;0;0;0;1;10;1;2;1;1;0;1293.00567;2606.87203;1;352.978911;80.6047503;1;1;0;18206.3943;0;67.7278468;18.8132908;0.24283543;359.307387;0;1;83;0;0;10;2;1.48159042;67.7365881;18.2063943;0;0;0;0 +83.5;0;0;0;0;1;10;1;2;1;1;0;1301.33759;2598.65794;1;354.134069;81.1241547;1;1;0;18149.027;0;68.1639922;18.9344423;0.241785967;361.62121;0;1;83.5;0;0;10;2;1.49282234;68.1726957;18.149027;0;0;0;0 +84;0;0;0;0;1;10;1;2;1;1;0;1309.63293;2590.13509;1;355.222625;81.641278;1;1;0;18089.5035;0;68.598218;19.0550606;0.240703411;363.92485;0;1;84;0;0;10;2;1.50412624;68.6068826;18.0895035;0;0;0;0 +84.5;0;0;0;0;1;10;1;2;1;1;0;1317.89054;2581.30928;1;356.244364;82.15605;1;1;0;18027.864;0;69.0304654;19.1751293;0.239588413;366.217993;0;1;84.5;0;0;10;2;1.5155018;69.0390898;18.027864;0;0;0;0 +85;0;0;0;0;1;10;1;2;1;1;0;1326.10933;2572.18642;1;357.199129;82.6684016;1;1;0;17964.15;0;69.4606765;19.2946324;0.23844164;368.500334;0;1;85;0;0;10;2;1.52694868;69.4692596;17.96415;0;0;0;0 +85.5;0;0;0;0;1;10;1;2;1;1;0;1334.28822;2562.77258;1;358.086821;83.1782656;1;1;0;17898.4037;0;69.8887948;19.4135541;0.237263774;370.771572;0;1;85.5;0;0;10;2;1.53846655;69.8973355;17.8984037;0;0;0;0 +86;0;0;0;0;1;10;1;2;1;1;0;1342.42614;2553.07392;1;358.907395;83.6855759;1;1;0;17830.6682;0;70.3147649;19.5318791;0.236055509;373.031414;0;1;86;0;0;10;2;1.55005505;70.3232621;17.8306682;0;0;0;0 +86.5;0;0;0;0;1;10;1;2;1;1;0;1350.52206;2543.09674;1;359.660863;84.190268;1;1;0;17760.9876;0;70.7385328;19.6495924;0.234817553;375.279572;0;1;86.5;0;0;10;2;1.56171383;70.7469853;17.7609876;0;0;0;0 +87;0;0;0;0;1;10;1;2;1;1;0;1358.57498;2532.84745;1;360.347293;84.6922791;1;1;0;17689.4066;0;71.1600456;19.7666793;0.233550623;377.515767;0;1;87;0;0;10;2;1.57344251;71.1684525;17.6894066;0;0;0;0 +87.5;0;0;0;0;1;10;1;2;1;1;0;1366.5839;2522.33256;1;360.966804;85.1915477;1;1;0;17615.9706;0;71.579252;19.8831255;0.232255448;379.739726;0;1;87.5;0;0;10;2;1.58524072;71.5876122;17.6159706;0;0;0;0 +88;0;0;0;0;1;10;1;2;1;1;0;1374.54787;2511.55867;1;361.519572;85.688014;1;1;0;17540.7258;0;71.9961016;19.9989171;0.230932769;381.951182;0;1;88;0;0;10;2;1.59710808;72.0044143;17.5407258;0;0;0;0 +88.5;0;0;0;0;1;10;1;2;1;1;0;1382.46595;2500.53247;1;362.005825;86.1816198;1;1;0;17463.7188;0;72.4105458;20.1140405;0.229583332;384.149876;0;1;88.5;0;0;10;2;1.60904419;72.4188098;17.4637188;0;0;0;0 +89;0;0;0;0;1;10;1;2;1;1;0;1390.33724;2489.26072;1;362.425844;86.6723084;1;1;0;17384.9969;0;72.822537;20.2284825;0.228207895;386.335558;0;1;89;0;0;10;2;1.62104865;72.8307515;17.3849969;0;0;0;0 +89.5;0;0;0;0;1;10;1;2;1;1;0;1398.16085;2477.75027;1;362.779959;87.160025;1;1;0;17304.6079;0;73.2320291;20.3422303;0.22680722;388.507981;0;1;89.5;0;0;10;2;1.63312106;73.2401931;17.3046079;0;0;0;0 +90;0;0;0;0;1;10;1;2;1;1;0;1405.93593;2466.00802;1;363.068551;87.6447161;1;1;0;17222.6;0;73.6389774;20.4552715;0.225382079;390.666909;0;1;90;0;0;10;2;1.64526098;73.6470901;17.2226;0;0;0;0 +90.5;0;0;0;0;0;0;1;2;0;1;0;1406.20903;-285.74677;0.00515377521;-42.0784595;0.451788908;1;1;0;-1995.65544;0;73.621809;20.4505025;-0.0661529104;390.575827;0;1;90.5;0;0;0;2;1.64744214;73.6194215;-1.99565544;0;0;0;0 +91;0;0;0;0;0;0;1;2;0;1;0;1403.89217;-299.926448;2.65613989e-05;-44.0937599;0.00232458218;1;1;0;-2094.68631;0;73.5003029;20.4167508;-0.067768788;389.931218;0;1;91;0;0;0;2;1.64745337;73.4978633;-2.09468631;0;0;0;0 +91.5;0;0;0;0;0;0;1;2;0;1;0;1401.56306;-299.99962;1.36891479e-07;-44.0313465;1.19604982e-05;1;1;0;-2095.19735;0;73.3783561;20.3828767;-0.0677231715;389.28427;0;1;91.5;0;0;0;2;1.64745343;73.3759181;-2.09519735;0;0;0;0 +92;0;0;0;0;0;0;1;2;0;1;0;1399.23575;-299.999998;7.05507911e-10;-43.9582872;6.15393619e-08;1;1;0;-2095.19999;0;73.256504;20.3490289;-0.0676691137;388.637824;0;1;92;0;0;0;2;1.64745343;73.254068;-2.09519999;0;0;0;0 +92.5;0;0;0;0;0;0;1;2;0;1;0;1396.91029;-300;3.63602918e-12;-43.885231;3.16632934e-10;1;1;0;-2095.2;0;73.1347492;20.3152081;-0.0676151446;387.991895;0;1;92.5;0;0;0;2;1.64745343;73.1323151;-2.0952;0;0;0;0 +93;0;0;0;0;0;0;1;2;0;1;0;1394.58669;-300;1.8739277e-14;-43.8122329;1.62914056e-12;1;1;0;-2095.2;0;73.0130914;20.2814143;-0.067561308;387.34648;0;1;93;0;0;0;2;1.64745343;73.0106592;-2.0952;0;0;0;0 +93.5;0;0;0;0;0;0;1;2;0;1;0;1392.26493;-300;9.65780214e-17;-43.7392928;8.38224589e-15;1;1;0;-2095.2;0;72.8915303;20.2476473;-0.0675076037;386.701579;0;1;93.5;0;0;0;2;1.64745343;72.8891001;-2.0952;0;0;0;0 +94;0;0;0;0;0;0;1;2;0;1;0;1389.94502;-300;4.97741412e-19;-43.6664106;4.31282272e-17;1;1;0;-2095.2;0;72.7700658;20.2139072;-0.0674540315;386.05719;0;1;94;0;0;0;2;1.64745343;72.7676375;-2.0952;0;0;0;0 +94.5;0;0;0;0;0;0;1;2;0;1;0;1387.62695;-300;2.56524735e-21;-43.5935862;2.21902493e-19;1;1;0;-2095.2;0;72.6486977;20.1801938;-0.067400591;385.413311;0;1;94.5;0;0;0;2;1.64745343;72.6462713;-2.0952;0;0;0;0 +95;0;0;0;0;0;0;1;2;0;1;0;1385.31071;-300;1.32207082e-23;-43.5208195;1.1417266e-21;1;1;0;-2095.2;0;72.5274256;20.1465071;-0.0673472818;384.769943;0;1;95;0;0;0;2;1.64745343;72.5250011;-2.0952;0;0;0;0 +95.5;0;0;0;0;0;0;1;2;0;1;0;1382.9963;-300;6.81365581e-26;-43.4481103;5.87437166e-24;1;1;0;-2095.2;0;72.4062493;20.112847;-0.0672941038;384.127083;0;1;95.5;0;0;0;2;1.64745343;72.4038267;-2.0952;0;0;0;0 +96;0;0;0;0;0;0;1;2;0;1;0;1380.68372;-300;3.51160504e-28;-43.3754585;3.02245663e-26;1;1;0;-2095.2;0;72.2851686;20.0792135;-0.0672410565;383.48473;0;1;96;0;0;0;2;1.64745343;72.282748;-2.0952;0;0;0;0 +96.5;0;0;0;0;0;0;1;2;0;1;0;1378.37297;-300;1.8098023e-30;-43.3028638;1.55509918e-28;1;1;0;-2095.2;0;72.1641833;20.0456065;-0.0671881397;382.842883;0;1;96.5;0;0;0;2;1.64745343;72.1617646;-2.0952;0;0;0;0 +97;0;0;0;0;0;0;1;2;0;1;0;1376.06403;-300;9.32731422e-33;-43.2303263;8.00120611e-31;1;1;0;-2095.2;0;72.0432931;20.0120259;-0.0671353531;382.201541;0;1;97;0;0;0;2;1.64745343;72.0408763;-2.0952;0;0;0;0 +97.5;0;0;0;0;0;0;1;2;0;1;0;1373.7569;-300;4.80708808e-35;-43.1578457;4.11672801e-33;1;1;0;-2095.2;0;71.9224979;19.9784716;-0.0670826963;381.560702;0;1;97.5;0;0;0;2;1.64745343;71.9200829;-2.0952;0;0;0;0 +98;0;0;0;0;0;0;1;2;0;1;0;1371.45158;-300;2.47746514e-37;-43.085422;2.11810867e-35;1;1;0;-2095.2;0;71.8017972;19.9449437;-0.067030169;380.920365;0;1;98;0;0;0;2;1.64745343;71.7993842;-2.0952;0;0;0;0 +98.5;0;0;0;0;0;0;1;2;0;1;0;1369.14806;-300;1.27682984e-39;-43.0130549;1.08979208e-37;1;1;0;-2095.2;0;71.6811911;19.911442;-0.0669777709;380.280529;0;1;98.5;0;0;0;2;1.64745343;71.6787799;-2.0952;0;0;0;0 +99;0;0;0;0;0;0;1;2;0;1;0;1366.84634;-300;6.58049397e-42;-42.9407443;5.60710126e-40;1;1;0;-2095.2;0;71.5606791;19.8779664;-0.0669255017;379.641194;0;1;99;0;0;0;2;1.64745343;71.5582698;-2.0952;0;0;0;0 +99.5;0;0;0;0;0;0;1;2;0;1;0;1364.54642;-300;3.39143867e-44;-42.8684901;2.88491147e-42;1;1;0;-2095.2;0;71.4402611;19.844517;-0.0668733611;379.002356;0;1;99.5;0;0;0;2;1.64745343;71.4378537;-2.0952;0;0;0;0 +100;0;0;0;0;0;0;1;2;0;1;0;1362.24829;-300;1.74787125e-46;-42.7962922;1.48431446e-44;1;1;0;-2095.2;0;71.3199368;19.8110935;-0.0668213489;378.364016;0;1;100;0;0;0;2;1.64745343;71.3175312;-2.0952;0;0;0;0 +100.5;0;0;0;0;0;0;1;2;0;1;0;1359.95194;-300;9.00813552e-49;-42.7241503;7.63692775e-47;1;1;0;-2095.2;0;71.199706;19.7776961;-0.0667694646;377.726172;0;1;100.5;0;0;0;2;1.64745343;71.1973023;-2.0952;0;0;0;0 +101;0;0;0;0;0;0;1;2;0;1;0;1357.65738;-300;4.64259055e-51;-42.6520645;3.92926009e-49;1;1;0;-2095.2;0;71.0795685;19.7443246;-0.066717708;377.088823;0;1;101;0;0;0;2;1.64745343;71.0771667;-2.0952;0;0;0;0 +101.5;0;0;0;0;0;0;1;2;0;1;0;1355.36459;-300;2.39268681e-53;-42.5800344;2.02163245e-51;1;1;0;-2095.2;0;70.959524;19.7109789;-0.0666660788;376.451967;0;1;101.5;0;0;0;2;1.64745343;70.9571241;-2.0952;0;0;0;0 +102;0;0;0;0;0;0;1;2;0;1;0;1353.07358;-300;1.233137e-55;-42.5080601;1.04014276e-53;1;1;0;-2095.2;0;70.8395724;19.677659;-0.0666145767;375.815604;0;1;102;0;0;0;2;1.64745343;70.8371743;-2.0952;0;0;0;0 +102.5;0;0;0;0;0;0;1;2;0;1;0;1350.78433;-300;6.35531087e-58;-42.4361414;5.35159233e-56;1;1;0;-2095.2;0;70.7197133;19.6443648;-0.0665632014;375.179732;0;1;102.5;0;0;0;2;1.64745343;70.7173171;-2.0952;0;0;0;0 +103;0;0;0;0;0;0;1;2;0;1;0;1348.49685;-300;3.27538436e-60;-42.364278;2.75341971e-58;1;1;0;-2095.2;0;70.5999466;19.6110963;-0.0665119525;374.54435;0;1;103;0;0;0;2;1.64745343;70.5975522;-2.0952;0;0;0;0 +103.5;0;0;0;0;0;0;1;2;0;1;0;1346.21113;-300;1.68805947e-62;-42.29247;1.41664531e-60;1;1;0;-2095.2;0;70.4802721;19.5778534;-0.0664608299;373.909456;0;1;103.5;0;0;0;2;1.64745343;70.4778795;-2.0952;0;0;0;0 +104;0;0;0;0;0;0;1;2;0;1;0;1343.92716;-300;8.69987905e-65;-42.2207171;7.28868458e-63;1;1;0;-2095.2;0;70.3606894;19.5446359;-0.0664098331;373.275051;0;1;104;0;0;0;2;1.64745343;70.3582987;-2.0952;0;0;0;0 +104.5;0;0;0;0;0;0;1;2;0;1;0;1341.64495;-300;4.4837221e-67;-42.1490192;3.75004515e-65;1;1;0;-2095.2;0;70.2411984;19.511444;-0.0663589619;372.641131;0;1;104.5;0;0;0;2;1.64745343;70.2388095;-2.0952;0;0;0;0 +105;0;0;0;0;0;0;1;2;0;1;0;1339.36448;-300;2.31080958e-69;-42.0773762;1.92940387e-67;1;1;0;-2095.2;0;70.1217989;19.4782775;-0.0663082161;372.007697;0;1;105;0;0;0;2;1.64745343;70.1194118;-2.0952;0;0;0;0 +105.5;0;0;1;1;0;0;1;2;0;0;0;1337.08576;-300;1.19093931e-71;-42.0057879;0;0.5;0;0;-2095.2;0;70.0024906;19.4451363;-0.0662575952;371.374747;0;1;105.5;0;1;0;2;1.64745343;70.0001054;-2.0952;0;0;0;0 +106;0;0;0;0;0;0;1;2;0;0;0;1319.73333;-300;6.1378335e-74;-41.4606454;0;0;0;0;0;0;69.8894791;19.4137442;-0.0377370968;370.775202;0;1;106;0;0;0;2;1.64745343;69.8882403;0;0;0;0;0 +106.5;0;0;0;0;0;0;1;2;0;0;0;1176.49388;-300;3.16330141e-76;-36.9606454;0;0;0;0;0;0;69.8275654;19.396546;-0.0343836314;370.44674;0;1;106.5;0;0;0;2;1.64745343;69.8263276;0;0;0;0;0 +107;0;0;0;0;0;0;1;2;0;0;0;1033.25443;-300;1.63029444e-78;-32.4606454;0;0;0;0;0;0;69.7656989;19.3793608;-0.0343574822;370.118528;0;1;107;0;0;0;2;1.64745343;69.7644621;0;0;0;0;0 +107.5;0;0;0;0;0;0;1;2;0;0;0;890.014984;-300;8.40217105e-81;-27.9606454;0;0;0;0;0;0;69.7038794;19.3621887;-0.0343313761;369.790565;0;1;107.5;0;0;0;2;1.64745343;69.7026435;0;0;0;0;0 +108;0;0;0;0;0;0;1;2;0;0;0;746.775536;-300;4.33029009e-83;-23.4606454;0;0;0;0;0;0;69.6421069;19.3450297;-0.0343053128;369.462852;0;1;108;0;0;0;2;1.64745343;69.6408719;0;0;0;0;0 +108.5;0;0;0;0;0;0;1;2;0;0;0;603.536087;-300;2.23173417e-85;-18.9606454;0;0;0;0;0;0;69.5803812;19.3278837;-0.0342792925;369.135387;0;1;108.5;0;0;0;2;1.64745343;69.5791472;0;0;0;0;0 +109;0;0;0;0;0;0;1;2;0;0;0;460.296638;-300;1.15018562e-87;-14.4606454;0;0;0;0;0;0;69.5187023;19.3107506;-0.0342533149;368.80817;0;1;109;0;0;0;2;1.64745343;69.5174692;0;0;0;0;0 +109.5;0;0;0;0;0;0;1;2;0;0;0;317.057189;-300;5.92779815e-90;-9.96064537;0;0;0;0;0;0;69.4570702;19.2936306;-0.03422738;368.481202;0;1;109.5;0;0;0;2;1.64745343;69.455838;0;0;0;0;0 +110;0;0;0;0;0;0;1;2;0;0;0;173.817741;-300;3.05505391e-92;-5.46064537;0;0;0;0;0;0;69.3954847;19.2765235;-0.0342014877;368.15448;0;1;110;0;0;0;2;1.64745343;69.3942534;0;0;0;0;0 +110.5;0;0;0;0;0;0;1;2;0;0;0;36.3111152;-181.555576;1.57450611e-94;-0.690363459;0;0;0;0;0;0;69.3339457;19.2594294;-0.0341756379;367.828006;0;1;110.5;0;0;0;2;1.64745343;69.3327154;0;0;0;0;0 +111;0;0;0;0;0;0;1;2;0;0;0;3.14534911;-15.7267455;8.11465056e-97;-0.0051800784;0;0;0;0;0;0;69.2724533;19.2423481;-0.0341498305;367.501779;0;1;111;0;0;0;2;1.64745343;69.2712239;0;0;0;0;0 +111.5;0;0;0;0;0;0;1;2;0;0;0;0.272457096;-1.36228548;4.18210849e-99;-3.88682395e-05;0;0;0;0;0;0;69.2110072;19.2252798;-0.0341240655;367.175797;0;1;111.5;0;0;0;2;1.64745343;69.2097788;0;0;0;0;0 +112;0;0;0;0;0;0;1;2;0;0;0;0.0236008363;-0.118004181;2.1553647e-101;-2.91644242e-07;0;0;0;0;0;0;69.1496075;19.2082243;-0.0340983428;366.850062;0;1;112;0;0;0;2;1.64745343;69.14838;0;0;0;0;0 +112.5;0;0;0;0;0;0;1;2;0;0;0;0.00204435664;-0.0102217832;1.11082652e-103;-2.18832561e-09;0;0;0;0;0;0;69.0882541;19.1911817;-0.0340726623;366.524572;0;1;112.5;0;0;0;2;1.64745343;69.0870275;0;0;0;0;0 +113;0;0;0;0;0;0;1;2;0;0;0;0.000177086693;-0.000885433467;5.72495016e-106;-1.6419899e-11;0;0;0;0;0;0;69.0269468;19.1741519;-0.0340470238;366.199327;0;1;113;0;0;0;2;1.64745343;69.0257212;0;0;0;0;0 +113.5;0;0;0;0;0;0;1;2;0;0;0;1.53396411e-05;-7.66982053e-05;2.95051062e-108;-1.23205194e-13;0;0;0;0;0;0;68.9656857;19.1571349;-0.0340214274;365.874326;0;1;113.5;0;0;0;2;1.64745343;68.964461;0;0;0;0;0 +114;0;0;0;0;0;0;1;2;0;0;0;1.32875364e-06;-6.64376818e-06;1.52062685e-110;-9.24458785e-16;0;0;0;0;0;0;68.9044706;19.1401307;-0.0339958729;365.54957;0;1;114;0;0;0;2;1.64745343;68.9032468;0;0;0;0;0 +114.5;0;0;0;0;0;0;1;2;0;0;0;1.15099579e-07;-5.75497894e-07;7.83696895e-113;-6.93659104e-18;0;0;0;0;0;0;68.8433015;19.1231393;-0.0339703603;365.225058;0;1;114.5;0;0;0;2;1.64745343;68.8420785;0;0;0;0;0 +115;0;0;0;0;0;0;1;2;0;0;0;9.97018008e-09;-4.98509004e-08;4.03899763e-115;-5.20480697e-20;0;0;0;0;0;0;68.7821782;19.1061606;-0.0339448894;364.900789;0;1;115;0;0;0;2;1.64745343;68.7809562;0;0;0;0;0 +115.5;0;0;0;0;0;0;1;2;0;0;0;8.6363905e-10;-4.31819525e-09;2.08160858e-117;-3.9053788e-22;0;0;0;0;0;0;68.7211008;19.0891947;-0.0339194603;364.576763;0;1;115.5;0;0;0;2;1.64745343;68.7198797;0;0;0;0;0 +116;0;0;0;0;0;0;1;2;0;0;0;7.48103246e-11;-3.74051623e-10;1.07281427e-119;-2.93036488e-24;0;0;0;0;0;0;68.660069;19.0722414;-0.0338940727;364.252979;0;1;116;0;0;0;2;1.64745343;68.6588489;0;0;0;0;0 +116.5;0;0;0;0;0;0;1;2;0;0;0;6.48023578e-12;-3.24011789e-11;5.52904359e-122;-2.1987722e-26;0;0;0;0;0;0;68.599083;19.0553008;-0.0338687267;363.929438;0;1;116.5;0;0;0;2;1.64745343;68.5978637;0;0;0;0;0 +117;0;0;0;0;0;0;1;2;0;0;0;5.61332356e-13;-2.80666178e-12;2.84954478e-124;-1.6498284e-28;0;0;0;0;0;0;68.5381425;19.0383729;-0.0338434221;363.606139;0;1;117;0;0;0;2;1.64745343;68.5369242;0;0;0;0;0 +117.5;0;0;0;0;0;0;1;2;0;0;0;4.86238502e-14;-2.43119251e-13;1.46859132e-126;-1.23793349e-30;0;0;0;0;0;0;68.4772475;19.0214577;-0.0338181588;363.283081;0;1;117.5;0;0;0;2;1.64745343;68.4760301;0;0;0;0;0 +118;0;0;0;0;0;0;1;2;0;0;0;4.21190545e-15;-2.10595272e-14;7.56878955e-129;-9.2887195e-33;0;0;0;0;0;0;68.416398;19.004555;-0.0337929369;362.960264;0;1;118;0;0;0;2;1.64745343;68.4151815;0;0;0;0;0 +118.5;0;0;0;0;0;0;1;2;0;0;0;3.64844565e-16;-1.82422283e-15;3.900784e-131;-6.96970482e-35;0;0;0;0;0;0;68.3555939;18.987665;-0.0337677561;362.637688;0;1;118.5;0;0;0;2;1.64745343;68.3543782;0;0;0;0;0 +119;0;0;0;0;0;0;1;2;0;0;0;3.16036432e-17;-1.58018216e-16;2.01037638e-133;-5.22965358e-37;0;0;0;0;0;0;68.294835;18.9707875;-0.0337426165;362.315352;0;1;119;0;0;0;2;1.64745343;68.2936203;0;0;0;0;0 +119.5;0;0;0;0;0;0;1;2;0;0;0;2.73757747e-18;-1.36878874e-17;1.0361028e-135;-3.92402223e-39;0;0;0;0;0;0;68.2341213;18.9539226;-0.0337175179;361.993256;0;1;119.5;0;0;0;2;1.64745343;68.2329075;0;0;0;0;0 +120;0;0;0;0;0;0;1;2;0;0;0;2.37135015e-19;-1.18567508e-18;5.33984091e-138;-2.94435381e-41;0;0;0;0;0;0;68.1734528;18.9370702;-0.0336924603;361.6714;0;1;120;0;0;0;2;1.64745343;68.1722399;0;0;0;0;0 +120.5;0;0;0;0;0;0;1;2;0;0;0;2.05411595e-20;-1.02705798e-19;2.75203397e-140;-2.20926866e-43;0;0;0;0;0;0;68.1128293;18.9202304;-0.0336674435;361.349783;0;1;120.5;0;0;0;2;1.64745343;68.1116173;0;0;0;0;0 +121;0;0;0;0;0;0;1;2;0;0;0;1.77932067e-21;-8.89660334e-21;1.41833644e-142;-1.65770432e-45;0;0;0;0;0;0;68.0522509;18.903403;-0.0336424676;361.028404;0;1;121;0;0;0;2;1.64745343;68.0510398;0;0;0;0;0 diff --git a/tools/vehicle_graph/tests/diesel-hydro.script b/tools/vehicle_graph/tests/diesel-hydro.script new file mode 100644 index 00000000..b38813a7 --- /dev/null +++ b/tools/vehicle_graph/tests/diesel-hydro.script @@ -0,0 +1,12 @@ +# events for: -graph-run diesel-hydro.vhg -script diesel-hydro.script +# options: -every 50 +# start the engine and wait at idle, drive off on a low controller position, then full power through the lockup speed, +# then the controller to zero (coasting, engine braking through the lockup clutch), then stop the engine +1.0 press starter +1.5 release starter +10.0 value throttle 0.3 +25.0 value throttle 1 +90.0 value throttle 0 +105.0 press stop +105.5 release stop +120.0 value throttle 0 diff --git a/tools/vehicle_graph/tests/diesel-hydro.vhg b/tools/vehicle_graph/tests/diesel-hydro.vhg new file mode 100644 index 00000000..73f9a84d --- /dev/null +++ b/tools/vehicle_graph/tests/diesel-hydro.vhg @@ -0,0 +1,35 @@ +// expect: ok +// diesel locomotive with a hydraulic transmission: engine, torque converter with a lockup clutch, final drive. +// start the engine, select the direction, raise the controller: the converter fills and the locomotive drives off; above +// the lockup speed the converter is bridged; with the controller at zero the locomotive coasts with the +// engine braking through the lockup clutch; stopping the engine releases the lockup clutch and drains the converter +{ + "format": "maszyna-vehicle-graph", + "version": 2, + "vehicle": { "name": "diesel hydraulic test" }, + "body": { "mass": 64000, "length": 14.2, "wheel_diameter": 1.0, "axle_arrangement": "B'B'", "power": 400, "vmax": 90, "bearing": "roll" }, + "nodes": [ + { "id": "starter", "type": "cab.switch", "params": { "behaviour": "monostable" } }, + { "id": "stop", "type": "cab.switch", "params": { "behaviour": "monostable" } }, + { "id": "throttle", "type": "cab.switch", "params": { "behaviour": "multi", "positions": 11, "min": 0, "max": 1, "default": 0 } }, + { "id": "reverser", "type": "cab.switch", "params": { "behaviour": "multi", "positions": 3, "min": -1, "max": 1, "default": 1 } }, + { "id": "power", "type": "logic.compare", "params": { "op": "gt", "value": 0 } }, + { "id": "engine", "type": "drive.diesel", "params": { "idle_rpm": 600, "max_rpm": 1500 } }, + { "id": "tc", "type": "drive.hydro", "params": { "idle_rpm": 600, "lockup_speed": 60, "unlock_speed": 50, "fill_rate": 0.5, "drain_rate": 1.0 } }, + { "id": "final", "type": "drive.gear", "params": { "ratio": 3.6, "eff": 0.97 } }, + { "id": "axles", "type": "drive.axle", "params": { "powered_axles": 1 } }, + { "id": "vs", "type": "veh.state" } + ], + "connections": [ + { "from": "starter.pos", "to": "engine.start" }, + { "from": "stop.pos", "to": "engine.stop" }, + { "from": "throttle.pos", "to": "engine.demand" }, + { "from": "throttle.pos", "to": "power.a" }, + { "from": "power.q", "to": "tc.power" }, + { "from": "engine.running", "to": "tc.running" }, + { "from": "reverser.pos", "to": "tc.dir" }, + { "from": "engine.shaft", "to": "tc.in" }, + { "from": "tc.out", "to": "final.in" }, + { "from": "final.out", "to": "axles.shaft" } + ] +} diff --git a/tools/vehicle_graph/tests/diesel-mech.expected.csv b/tools/vehicle_graph/tests/diesel-mech.expected.csv new file mode 100644 index 00000000..3fbf2676 --- /dev/null +++ b/tools/vehicle_graph/tests/diesel-mech.expected.csv @@ -0,0 +1,213 @@ +time;starter.pos;starter.index;throttle.pos;throttle.index;gear.pos;gear.index;pedal.pos;pedal.index;reverser.pos;reverser.index;engine.running;engine.starting;engine.rpm;engine.torque;engine.fill;engine.power;engine.fuel_rate;box.actual;box.engage;box.slip;box.shifting;axles.force;axles.slip;vs.v;vs.v_signed;vs.accel;vs.wheel_rpm;vs.slipping;vs.cab;vs.time;starter:position;throttle:position;gear:position;pedal:position;reverser:position;engine:fuel_used;body:v;body:ft;body:fb;body:pg;body:zg;body:bc +0.5;0;0;0;0;0;0;1;2;1;2;0;0;0;0;0;0;0;0;0.217776325;0;0;0;0;0;0;0;0;0;1;0.5;0;0;0;2;2;0;0;0;0;0;0;0 +1;0;0;0;0;0;0;1;2;1;2;0;0;0;0;0;0;0;0;0.391185491;0;0;0;0;0;0;0;0;0;1;1;0;0;0;2;2;0;0;0;0;0;0;0 +1.5;1;1;0;0;0;0;1;2;1;2;0;1;94.1596943;93.9309908;0;-2.95811404;0;0;0.526152023;0;0;0;0;0;0;0;0;0;1;1.5;1;0;0;2;2;0;0;0;0;0;0;0 +2;0;0;0;0;0;0;1;2;1;2;0;1;127.576789;51.1318276;0;-4.00794303;0;0;0.631198169;0;0;0;0;0;0;0;0;0;1;2;0;0;0;2;2;0;0;0;0;0;0;0 +2.5;0;0;0;0;0;0;1;2;1;2;0;1;146.672895;30.6059411;0;-4.6078649;0;0;0.712956904;0;0;0;0;0;0;0;0;0;1;2.5;0;0;0;2;2;0;0;0;0;0;0;0 +3;0;0;0;0;0;0;1;2;1;2;1;1;158.368911;97.5109825;0.1;-3.67620849;0.987256811;0;0.776590754;0;0;0;0;0;0;0;0;0;1;3;0;0;0;2;2;2.74238003e-06;0;0;0;0;0;0 +3.5;0;0;0;0;0;0;1;2;1;2;1;0;677.707926;503.756274;0.314683506;35.7512846;13.2946453;0;0.826117778;0;0;0;0;0;0;0;0;0;1;3.5;0;0;0;2;2;0.00241944164;0;0;0;0;0;0 +4;0;0;0;0;0;0;1;2;1;2;1;0;611.528056;-296.102004;0.00162180805;-18.9620965;0.0618266973;0;0.864665283;0;0;0;0;0;0;0;0;0;1;4;0;0;0;2;2;0.00275627066;0;0;0;0;0;0 +4.5;0;0;0;0;0;0;1;2;1;2;1;0;589.194397;-8.88795853;0.123999137;-0.548389645;4.55446617;0;1;0;0;0;0;0;0;0;0;0;1;4.5;0;0;0;2;2;0.0033188495;0;0;0;0;0;0 +5;0;0;0;0;0;0;1;2;1;2;1;0;590.418379;2.1455467;0.12852792;0.132655866;4.73061449;0;1;0;0;0;0;0;0;0;0;0;1;5;0;0;0;2;2;0.00397720921;0;0;0;0;0;0 +5.5;0;0;0;0;0;0;1;2;1;2;1;0;590.437332;-0.135942722;0.127554791;-0.00840540007;4.69494807;0;1;0;0;0;0;0;0;0;0;0;1;5.5;0;0;0;2;2;0.00462957007;0;0;0;0;0;0 +6;0;0;0;0;0;0;1;2;1;2;1;0;590.428291;-0.000897745739;0.127613486;-5.55071757e-05;4.69703656;0;1;0;0;0;0;0;0;0;0;0;1;6;0;0;0;2;2;0.00528190307;0;0;0;0;0;0 +6.5;0;0;0;0;0;0;1;2;1;2;1;0;590.428976;0.000897415629;0.127614155;5.54868295e-05;4.69706663;0;1;0;0;0;0;0;0;0;0;0;1;6.5;0;0;0;2;2;0.00593427465;0;0;0;0;0;0 +7;0;0;0;0;0;0;1;2;1;2;1;0;590.42897;-7.39056895e-05;0.127613743;-4.56955753e-06;4.6970514;0;1;0;0;0;0;0;0;0;0;0;1;7;0;0;0;2;2;0.006586643;0;0;0;0;0;0 +7.5;0;0;0;0;0;0;1;2;1;2;1;0;590.428967;1.13081643e-06;0.127613775;6.99179012e-08;4.69705257;0;1;0;0;0;0;0;0;0;0;0;1;7.5;0;0;0;2;2;0.00723901139;0;0;0;0;0;0 +8;0;0;0;0;0;0;1;2;1;2;1;0;590.428967;3.44821217e-07;0.127613775;2.13201499e-08;4.69705256;0;1;0;0;0;0;0;0;0;0;0;1;8;0;0;0;2;2;0.00789137981;0;0;0;0;0;0 +8.5;0;0;0;0;0;0;1;2;1;2;1;0;590.428967;-3.72784825e-08;0.127613774;-2.3049128e-09;4.69705255;0;1;0;0;0;0;0;0;0;0;0;1;8.5;0;0;0;2;2;0.00854374822;0;0;0;0;0;0 +9;0;0;0;0;0;0;1;2;1;2;1;0;590.428967;1.21775656e-09;0.127613774;7.52933729e-11;4.69705255;0;1;0;0;0;0;0;0;0;0;0;1;9;0;0;0;2;2;0.00919611663;0;0;0;0;0;0 +9.5;0;0;0.5;5;0;0;1;2;1;2;1;0;1183.39934;-39.9116214;0.0872015647;-4.94605924;6.43303347;0;1;0;0;0;0;0;0;0;0;0;1;9.5;0;5;0;2;2;0.0133821834;0;0;0;0;0;0 +10;0;0;0.5;5;0;0;1;2;1;2;1;0;1064.90363;-298.653132;0.000449417262;-33.3047364;0.0298346033;0;1;0;0;0;0;0;0;0;0;0;1;10;0;5;0;2;2;0.0135401677;0;0;0;0;0;0 +10.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;1041.12655;17.2318321;0.10605026;1.87872664;6.88296353;0;0.636330477;0;1;0;0;0;0;0;0;0;1;10.5;0;5;1;1;2;0.0143130553;0;0;0;0;0;0 +11;0;0;0.5;5;1;1;0.5;1;1;2;1;0;1042.5668;-1.62454892;0.0997326298;-0.177363929;6.48188573;0;0.404916476;0;1;0;0;0;0;0;0;0;1;11;0;5;1;1;2;0.0152261233;0;0;0;0;0;0 +11.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;1042.47408;0.137259971;0.100322412;0.0149843458;6.51963736;0;0.257660694;0;1;0;0;0;0;0;0;0;1;11.5;0;5;1;1;2;0.0161307358;0;0;0;0;0;0 +12;0;0;0.5;5;1;1;0.5;1;1;2;1;0;1042.47953;-0.0107174577;0.100272897;-0.00117000558;6.51645361;0;0.163957353;0;1;0;0;0;0;0;0;0;1;12;0;5;1;1;2;0.0170358567;0;0;0;0;0;0 +12.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;1041.78739;4.3931521;0.101751723;0.479274096;6.60816806;1;0.00992524969;0;0;823.150583;0;0;0;0;0;0;1;12.5;0;5;1;1;2;0.0179413362;0;0.823150583;0;0;0;0 +13;0;0;0.5;5;1;1;0.5;1;1;2;1;0;1025.80078;570.409405;0.291473731;61.2742948;18.639002;1;0.118568668;1;0;9833.49247;0;0.118113942;0.0328094284;0.136703119;0.626613926;0;1;13;0;5;1;1;2;0.0197488527;0.123127724;9.83349247;0;0;0;0 +13.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;1015.68663;996.449354;0.434725075;105.9848;27.5254633;1;0.203127205;1;0;16846.3547;0;0.473000612;0.131389059;0.25058731;2.50934619;0;1;13.5;0;5;1;1;2;0.0229935658;0.482093725;16.8463547;0;0;0;0 +14;0;0;0.5;5;1;1;0.5;1;1;2;1;0;1007.61996;1329.89345;0.547189277;140.327318;34.3711884;1;0.268940175;1;0;22304.5535;0;1.00875308;0.280209188;0.339222928;5.35160128;0;1;14;0;5;1;1;2;0.0273224994;1.02102111;22.3045535;0;0;0;0 +14.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;1001.33664;1589.24781;0.634899607;166.648093;39.6319439;1;0.320163237;1;0;26552.7381;0;1.68527073;0.468130759;0.408205533;8.94063892;0;1;14.5;0;5;1;1;2;0.0324847745;1.70000972;26.5527381;0;0;0;0 +15;0;0;0.5;5;1;1;0.5;1;1;2;1;0;996.434408;1791.13302;0.703330848;186.898224;43.6886466;1;0.360030789;1;0;29859.1535;0;2.47133944;0.686483178;0.461890002;13.1108629;0;1;15;0;5;1;1;2;0.0382886962;2.4880014;29.8591535;0;0;0;0 +15.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;992.611859;1948.26804;0.756694651;202.514808;46.8231182;1;0.5;1;0;41467.5;0;3.34266124;0.928517011;0.503665701;17.7333686;0;1;15.5;0;5;1;1;2;0.0445878983;3.36607518;41.4675;0;0;0;0 +16;0;0;0.5;5;1;1;0.5;1;1;2;1;0;983.585038;2486.76374;0.939532041;256.138615;57.6081221;1;0.5;1;0;41467.5;0;4.5133349;1.25370414;0.650360004;23.9439853;0;1;16;0;5;1;1;2;0.0524937388;4.53674784;41.4675;0;0;0;0 +16.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;978.764372;2502.22464;0.945657672;256.46793;57.6995348;1;0.5;1;0;41467.5;0;5.68395113;1.57887531;0.650324027;30.1542973;0;1;16.5;0;5;1;1;2;0.0604766958;5.70736276;41.4675;0;0;0;0 +17;0;0;0.5;5;1;1;0.5;1;1;2;1;0;979.16793;2499.7278;0.944737208;256.317654;57.6671396;1;0.5;1;0;41467.5;0;6.85449498;1.90402638;0.650279755;36.3642253;0;1;17;0;5;1;1;2;0.0684888229;6.87790501;41.4675;0;0;0;0 +17.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;979.134937;2500.02919;0.944845265;256.33992;57.6717922;1;0.5;1;0;41467.5;0;8.02495152;2.2291532;0.650227189;42.5736901;0;1;17.5;0;5;1;1;2;0.076498547;8.04835966;41.4675;0;0;0;0 +18;0;0;0.5;5;1;1;0.5;1;1;2;1;0;979.137569;2499.9971;0.944833929;256.337319;57.6712553;1;0.5;1;0;41467.5;0;9.19530584;2.55425162;0.650166334;48.7826126;0;1;18;0;5;1;1;2;0.0845084648;9.21871178;41.4675;0;0;0;0 +18.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;979.137365;2500.00027;0.944835041;256.337591;57.6713111;1;0.5;1;0;41467.5;0;10.365543;2.8793175;0.650097191;54.9909136;0;1;18.5;0;5;1;1;2;0.0925183673;10.3889465;41.4675;0;0;0;0 +19;0;0;0.5;5;1;1;0.5;1;1;2;1;0;979.13738;2499.99998;0.944834937;256.337564;57.6713056;1;0.5;1;0;41467.5;0;11.5356481;3.2043467;0.650019765;61.198514;0;1;19;0;5;1;1;2;0.100528271;11.5590488;41.4675;0;0;0;0 +19.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;979.137379;2500;0.944834946;256.337567;57.6713061;1;0.5;1;0;41467.5;0;12.7056063;3.52933507;0.649934059;67.4053347;0;1;19.5;0;5;1;1;2;0.108538175;12.7290038;41.4675;0;0;0;0 +20;0;0;0.5;5;1;1;0.5;1;1;2;1;0;979.137379;2500;0.944834945;256.337566;57.6713061;1;0.5;1;0;41467.5;0;13.8754025;3.85427848;0.649840078;73.6112967;0;1;20;0;5;1;1;2;0.116548078;13.8987967;41.4675;0;0;0;0 +20.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;979.137379;2500;0.944834945;256.337566;57.6713061;1;0.5;1;0;41467.5;0;15.045022;4.17917279;0.649737826;79.8163209;0;1;20.5;0;5;1;1;2;0.124557982;15.0684125;41.4675;0;0;0;0 +21;0;0;0.5;5;1;1;0.5;1;1;2;1;0;979.137379;2500;0.944834945;256.337566;57.6713061;1;0.5;1;0;41467.5;0;16.2144499;4.50401387;0.649627308;86.0203285;0;1;21;0;5;1;1;2;0.132567886;16.2378364;41.4675;0;0;0;0 +21.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;979.137379;2500;0.944834945;256.337566;57.6713061;1;0.5;1;0;41467.5;0;17.3836713;4.82879758;0.649508531;92.2232404;0;1;21.5;0;5;1;1;2;0.140577789;17.4070535;41.4675;0;0;0;0 +22;0;0;0.5;5;1;1;0.5;1;1;2;1;0;979.137379;2500;0.944834945;256.337566;57.6713061;1;0.5;1;0;41467.5;0;18.5526713;5.15351979;0.649381501;98.4249779;0;1;22;0;5;1;1;2;0.148587693;18.5760489;41.4675;0;0;0;0 +22.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;979.137379;2500;0.944834945;256.337566;57.6713061;1;0.5;1;0;41467.5;0;19.721435;5.47817639;0.649246223;104.625462;0;1;22.5;0;5;1;1;2;0.156597596;19.7448078;41.4675;0;0;0;0 +23;0;0;0.5;5;1;1;0.5;1;1;2;1;0;993.779093;2219.84401;0.847920491;231.015399;52.5297332;1;0.5;0;0;36820.5525;0;20.877466;5.79929612;0.582589672;110.758397;0;1;23;0;5;1;1;2;0.164530715;20.8981306;36.8205525;0;0;0;0 +23.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;1027.02222;777.935294;0.360913546;83.6665637;23.1069822;1;0.5;0;0;12903.6127;0;21.5312123;5.98089232;0.192315032;114.226629;0;1;23.5;0;5;1;1;2;0.169539088;21.5379615;12.9036127;0;0;0;0 +24;0;0;0.5;5;1;1;0.5;1;1;2;1;0;1036.90578;267.75032;0.189880015;29.0735399;12.2737921;1;0.5;0;0;4441.17456;0;21.7226799;6.03407776;0.0520729238;115.242396;0;1;24;0;5;1;1;2;0.171828078;21.7245056;4.44117456;0;0;0;0 +24.5;0;0;0.5;5;1;1;0.5;1;1;2;1;0;1039.55443;128.726502;0.143345281;14.0134097;9.28946794;1;0.5;0;0;2135.1865;0;21.7739087;6.04830798;0.0138076895;115.514174;0;1;24.5;0;5;1;1;2;0.17327714;21.7743928;2.1351865;0;0;0;0 +25;0;0;0.5;5;1;1;0.5;1;1;2;1;0;1040.25578;91.8308638;0.130999944;10.0036255;8.49515792;1;0.5;0;0;1523.19854;0;21.7874711;6.0520753;0.00365078843;115.586124;0;1;25;0;5;1;1;2;0.174499218;21.7875991;1.52319854;0;0;0;0 +25.5;0;0;1;10;1;1;1;2;1;2;1;0;1085.02302;2686.00584;0.995521369;305.192968;67.3363773;1;0.610843721;0;0;44552.7789;0;22.8000306;6.33334185;0.698278253;120.957919;0;1;25.5;0;10;1;2;2;0.182255587;22.8251838;44.5527789;0;0;0;0 +26;0;0;1;10;1;1;1;2;1;2;1;0;1145.27889;2694.80543;0.999976918;323.196975;71.3939549;1;0.697114782;0;0;44698.7376;0;24.0623755;6.68399321;0.700952788;127.654867;0;1;26;0;10;1;2;2;0.191908226;24.0876072;44.6987376;0;0;0;0 +26.5;0;0;1;10;1;1;1;2;1;2;1;0;1205.31861;2672.26962;0.999999881;337.295661;75.1384131;1;0.764260631;0;0;44324.9362;0;25.319193;7.03310916;0.694822685;134.322491;0;1;26.5;0;10;1;2;2;0.202089723;25.3442004;44.3249362;0;0;0;0 +27;0;0;1;10;1;1;1;2;1;2;1;0;1264.64736;2632.22812;0.999999999;348.595345;78.8369202;1;0.816521089;0;0;43660.7678;0;26.560611;7.37794751;0.683987084;140.908418;0;1;27;0;10;1;2;2;0.212788249;26.5852251;43.6607678;0;0;0;0 +27.5;0;0;1;10;1;1;1;2;1;2;1;0;1322.8723;2575.81984;1;356.830547;82.4666082;1;0.85719606;0;0;42725.1237;0;27.7784353;7.71623202;0.668752653;147.369176;0;1;27.5;0;10;1;2;2;0.223995856;27.8024979;42.7251237;0;0;0;0 +28;0;0;1;10;1;1;1;2;1;2;1;0;1379.62997;2504.51771;1;361.838974;86.0048272;1;0.8888539;0;0;41542.4352;0;28.9650954;8.04585982;0.649514056;153.664603;0;1;28;0;10;1;2;2;0.235701357;28.9884627;41.5424352;0;0;0;0 +28.5;0;0;1;10;1;1;1;2;1;2;1;0;1434.54;2286.901;0.950994006;343.548965;85.0453654;1;1;0;0;37932.8268;0;30.1101196;8.3639221;0.597354157;159.739146;0;1;28.5;0;10;1;2;2;0.247843542;30.1313798;37.9328268;0;0;0;0 +29;0;0;1;10;1;1;1;2;1;2;1;0;1470.39113;912.653483;0.456395032;140.529469;41.834443;1;1;0;0;15138.1833;0;30.820877;8.56135473;0.227082226;163.509831;0;1;29;0;10;1;2;2;0.25641004;30.828873;15.1381833;0;0;0;0 +29.5;0;0;1;10;1;1;1;2;1;2;1;0;1482.84342;356.802068;0.249395402;55.4052955;23.0538757;1;1;0;0;5918.27591;0;31.0648523;8.62912564;0.0747239673;164.80416;0;1;29.5;0;10;1;2;2;0.260660187;31.0674834;5.91827591;0;0;0;0 +30;0;0;1;10;1;1;1;2;1;2;1;0;1486.95199;174.747801;0.180810129;27.2105478;16.7602276;1;1;0;0;2898.54178;0;31.1453899;8.65149719;0.0248172552;165.231425;0;1;30;0;10;1;2;2;0.263337818;31.1462639;2.89854178;0;0;0;0 +30.5;0;0;1;10;2;2;1;2;1;2;1;0;1488.32034;114.465298;0.158010831;17.8401655;14.6603211;1;0.636330477;0;1;1898.63589;0;31.1722243;8.6589512;0.00829603268;165.373786;0;1;30.5;0;10;2;2;2;0.26549079;31.1725165;1.89863589;0;0;0;0 +31;0;0;1;10;2;2;1;2;1;2;1;0;1488.77824;94.3359011;0.150387699;14.7073908;13.9573361;1;0.404916476;0;1;1564.74959;0;31.1812055;8.66144598;0.00277985871;165.421433;0;1;31;0;10;2;2;2;0.267468431;31.1813034;1.56474959;0;0;0;0 +31.5;0;0;1;10;2;2;1;2;1;2;1;0;1488.93173;87.5934087;0.147833143;13.6576139;13.7216648;1;0.257660694;0;1;1452.91187;0;31.1842162;8.66228229;0.000932239157;165.437405;0;1;31.5;0;10;2;2;2;0.269387335;31.1842491;1.45291187;0;0;0;0 +32;0;0;1;10;2;2;1;2;1;2;1;0;1488.98321;85.3325657;0.146976441;13.305562;13.6426186;1;0.163957353;0;1;1415.41127;0;31.185226;8.66256279;0.000312716219;165.442762;0;1;32;0;10;2;2;2;0.271286544;31.185237;1.41541127;0;0;0;0 +32.5;0;0;1;10;2;2;1;2;1;2;1;0;1490.5812;67.6896917;0.140413759;10.5659072;13.0474462;2;0.0198504994;0;0;1097.53411;0;31.1832615;8.66201709;-0.00923033273;165.43234;0;1;32.5;0;10;2;2;2;0.273175474;31.1830876;1.09753411;0;0;0;0 +33;0;0;1;10;2;2;1;2;1;2;1;0;1454.88231;1128.48715;0.531996006;171.930568;48.2499028;2;0.237137336;1;0;13111.3233;0;31.3530515;8.70918098;0.1858358;166.333104;0;1;33;0;10;2;2;2;0.277464506;31.3598649;13.1113233;0;0;0;0 +33.5;0;0;1;10;2;2;1;2;1;2;1;0;1433.26477;1991.75402;0.841833492;298.944624;75.2164506;2;0.406254409;1;0;22461.8063;0;31.8325761;8.84238224;0.337591539;168.877061;0;1;33.5;0;10;2;2;2;0.286185944;31.8448252;22.4618063;0;0;0;0 +34;0;0;1;10;2;2;1;2;1;2;1;0;1396.79797;2472.88974;0.99752222;361.715372;86.8593116;2;0.537880351;1;0;29739.4046;0;32.5530489;9.04251357;0.455635941;172.699288;0;1;34;0;10;2;2;2;0.297850198;32.5695263;29.7394046;0;0;0;0 +34.5;0;0;1;10;2;2;1;2;1;2;1;0;1215.08072;2666.85319;0.99998723;339.33828;75.7460153;2;0.640326474;1;0;35403.6508;0;33.4609278;9.29470216;0.5474393;177.515735;0;1;34.5;0;10;2;2;2;0.30924403;33.4806936;35.4036508;0;0;0;0 +35;0;0;1;10;2;2;1;2;1;2;1;0;1092.64207;2699.86445;0.999999934;308.921758;68.1142687;2;0.720061578;0;0;29855.1011;0;34.3759573;9.54887703;0.459207241;182.370118;0;1;35;0;10;2;2;2;0.318894072;34.3924888;29.8551011;0;0;0;0 +35.5;0;0;1;10;2;2;1;2;1;2;1;0;1118.94796;2699.10244;1;316.269908;69.7541572;2;0.782120411;0;0;29846.6747;0;35.2023503;9.77843063;0.458909437;186.754268;0;1;35.5;0;10;2;2;2;0.328470557;35.2188706;29.8466747;0;0;0;0 +36;0;0;1;10;2;2;1;2;1;2;1;0;1145.22075;2694.88771;1;323.190437;71.3919787;2;0.83042158;0;0;29800.0683;0;36.0276367;10.0076769;0.45799075;191.132549;0;1;36;0;10;2;2;2;0.338274683;36.0441235;29.8000683;0;0;0;0 +36.5;0;0;1;10;2;2;1;2;1;2;1;0;1171.425;2687.24617;1;329.648087;73.0255268;2;0.868014986;0;0;29715.5682;0;36.850703;10.2363064;0.456456011;195.499052;0;1;36.5;0;10;2;2;2;0.348306008;36.8671341;29.7155682;0;0;0;0 +37;0;0;1;10;2;2;1;2;1;2;1;0;1197.5256;2676.2219;1;335.610502;74.6526131;2;1;0;0;29593.6617;0;37.6704473;10.4640131;0.454313312;199.84793;0;1;37;0;10;2;2;2;0.358563781;37.6868008;29.5936617;0;0;0;0 +37.5;0;0;1;10;2;2;1;2;1;2;1;0;1223.48798;2661.8768;1;341.048603;76.2710834;2;1;0;0;29435.0336;0;38.485785;10.6904958;0.451573957;204.17343;0;1;37.5;0;10;2;2;2;0.369046953;38.5020394;29.4350336;0;0;0;0 +38;0;0;1;10;2;2;1;2;1;2;1;0;1249.27832;2644.28996;1;345.936906;77.8788289;2;1;0;0;29240.5584;0;39.2956549;10.9154597;0.448252332;208.469924;0;1;38;0;10;2;2;2;0.379754178;39.3117894;29.2405584;0;0;0;0 +38.5;0;0;1;10;2;2;1;2;1;2;1;0;1274.86369;2623.55672;1;350.253778;79.4737967;2;1;0;0;29011.2903;0;40.0990245;11.1386179;0.444365749;212.731932;0;1;38.5;0;10;2;2;2;0.390683818;40.1150187;29.0112903;0;0;0;0 +39;0;0;1;10;2;2;1;2;1;2;1;0;1300.21223;2599.78766;1;353.981644;81.0540005;2;1;0;0;28748.452;0;40.8948948;11.359693;0.439934253;216.954155;0;1;39;0;10;2;2;2;0.401833958;40.9107291;28.748452;0;0;0;0 +39.5;0;0;1;10;2;2;1;2;1;2;1;0;1325.29328;2573.10735;1;357.107127;82.6175295;2;1;0;0;28453.421;0;41.6823054;11.5784182;0.434980407;221.131498;0;1;39.5;0;10;2;2;2;0.413202412;41.6979609;28.453421;0;0;0;0 +40;0;0;1;10;2;2;1;2;1;2;1;0;1350.07755;2543.65305;1;359.621134;84.1625573;2;1;0;0;28127.7155;0;42.4603384;11.7945384;0.429529051;225.259091;0;1;40;0;10;2;2;2;0.424786733;42.4757973;28.1277155;0;0;0;0 +40.5;0;0;1;10;2;2;1;2;1;2;1;0;1374.53721;2511.5733;1;361.518875;85.6873495;2;1;0;0;27772.9776;0;43.2281227;12.0078119;0.423607046;229.332314;0;1;40.5;0;10;2;2;2;0.436584227;43.2433682;27.7729776;0;0;0;0 +41;0;0;1;10;2;2;1;2;1;2;1;0;1398.64603;2477.02637;1;362.799822;87.1902706;2;1;0;0;27390.9577;0;43.9848373;12.2180104;0.417243002;233.346809;0;1;41;0;10;2;2;2;0.448591966;43.9998533;27.3909577;0;0;0;0 +41.5;0;0;1;10;2;2;1;2;1;2;1;0;1422.37945;2440.17873;1;363.467617;88.6697897;2;1;0;0;26983.4963;0;44.7297138;12.4249205;0.410466996;237.298502;0;1;41.5;0;10;2;2;2;0.460806805;44.7444856;26.9834963;0;0;0;0 +42;0;0;1;10;2;2;1;2;1;2;1;0;1443.86494;1848.58847;0.794481075;279.508763;71.5105857;2;1;0;0;20441.6913;0;45.3937905;12.6093863;0.307004436;240.821538;0;1;42;0;10;2;2;2;0.472192388;45.4047505;20.4416913;0;0;0;0 +42.5;0;0;1;10;2;2;1;2;1;2;1;0;1458.52444;1263.67144;0.583753422;193.008527;53.0766322;2;1;0;0;13973.6788;0;45.842841;12.7341225;0.201488297;243.203825;0;1;42.5;0;10;2;2;2;0.480736457;45.850034;13.9736788;0;0;0;0 +43;0;0;1;10;2;2;1;2;1;2;1;0;1468.16224;883.55965;0.44475639;135.8434;40.705815;2;1;0;0;9770.40261;0;46.1381498;12.8161527;0.132901291;244.770487;0;1;43;0;10;2;2;2;0.487174281;46.1428949;9.77040261;0;0;0;0 +43.5;0;0;1;10;2;2;1;2;1;2;1;0;1474.53285;635.128303;0.352970129;98.071888;32.4453612;2;1;0;0;7023.24878;0;46.3334068;12.8703908;0.088081603;245.806358;0;1;43.5;0;10;2;2;2;0.492204284;46.3365519;7.02324878;0;0;0;0 +44;0;0;1;10;2;2;1;2;1;2;1;0;1478.76121;471.519058;0.292092716;73.0173248;26.9264429;2;1;0;0;5214.05774;0;46.4630306;12.9063974;0.0585680723;246.494033;0;1;44;0;10;2;2;2;0.496293999;46.4651219;5.21405774;0;0;0;0 +44.5;0;0;1;10;2;2;1;2;1;2;1;0;1481.57555;363.199655;0.251593168;56.3505095;23.2371497;2;1;0;0;4016.26178;0;46.5493182;12.9303662;0.0390298097;246.951803;0;1;44.5;0;10;2;2;2;0.499755324;46.550712;4.01626178;0;0;0;0 +45;0;0;1;10;2;2;1;2;1;2;1;0;1483.45228;291.226549;0.224595087;45.2411007;20.7698827;2;1;0;0;3220.38317;0;46.606864;12.9463511;0.0260482438;247.257093;0;1;45;0;10;2;2;2;0.502796493;46.6077942;3.22038317;0;0;0;0 +45.5;0;0;1;10;3;3;1;2;1;2;1;0;1484.70536;243.286943;0.206572632;37.825763;19.1193565;2;0.636330477;0;1;2690.26701;0;46.6452893;12.9570248;0.0174018332;247.460946;0;1;45.5;0;10;3;2;2;0.505556633;46.6459108;2.69026701;0;0;0;0 +46;0;0;1;10;3;3;1;2;1;2;1;0;1485.54275;211.302866;0.194530676;32.8714727;18.0149666;2;0.404916476;0;1;2336.5871;0;46.6709686;12.9641579;0.0116333077;247.597178;0;1;46;0;10;3;2;2;0.508128752;46.671384;2.3365871;0;0;0;0 +46.5;0;0;1;10;3;3;1;2;1;2;1;0;1486.10267;189.940256;0.186479675;29.5593208;17.2758939;2;0.257660694;0;1;2100.35935;0;46.6881394;12.9689276;0.00778049117;247.688272;0;1;46.5;0;10;3;2;2;0.510575055;46.6884172;2.10035935;0;0;0;0 +47;0;0;1;10;3;3;1;2;1;2;1;0;1486.4772;175.66125;0.181094702;27.3440499;16.781246;2;0.163957353;0;1;1942.4621;0;46.6996252;12.9721181;0.00520525361;247.749206;0;1;47;0;10;3;2;2;0.512937156;46.699811;1.9424621;0;0;0;0 +47.5;0;0;1;10;3;3;1;2;1;2;1;0;1491.1625;124.888638;0.162327383;19.5018819;15.0895737;3;0.0198504994;0;0;768.273877;0;46.7036992;12.9732498;-0.0168907185;247.77082;0;1;47.5;0;10;3;2;2;0.515234695;46.703202;0.768273877;0;0;0;0 +48;0;0;1;10;3;3;1;2;1;2;1;0;1454.90621;1124.38277;0.530475839;171.308061;48.1128205;3;0.237137336;1;0;9177.92631;0;46.8037777;13.0010493;0.119650204;248.301752;0;1;48;0;10;3;2;2;0.519513252;46.8081713;9.17792631;0;0;0;0 +48.5;0;0;1;10;3;3;1;2;1;2;1;0;1433.26181;1991.98566;0.841917052;298.978772;75.2237611;3;0.406254409;1;0;15723.2644;0;47.1206413;13.089067;0.225853677;249.982766;0;1;48.5;0;10;3;2;2;0.528234614;47.1288391;15.7232644;0;0;0;0 +49;0;0;1;10;3;3;1;2;1;2;1;0;1396.79451;2472.89323;0.997521632;361.714986;86.859045;3;0.537880351;1;0;20817.5832;0;47.6061081;13.2239189;0.308444095;252.558248;0;1;49;0;10;3;2;2;0.539898832;47.6172643;20.8175832;0;0;0;0 +49.5;0;0;1;10;3;3;1;2;1;2;1;0;1215.07857;2666.85441;0.999987227;339.337836;75.7458812;3;0.640326474;1;0;24782.5555;0;48.2226754;13.3951876;0.37265476;255.829239;0;1;49.5;0;10;3;2;2;0.55129264;48.2361315;24.7825555;0;0;0;0 +50;0;0;1;10;3;3;1;2;1;2;1;0;1087.58221;2699.6143;0.999999934;307.462698;67.7988421;3;0.720061578;0;0;20896.6344;0;48.8441432;13.5678176;0.310810273;259.126228;0;1;50;0;10;3;2;2;0.560935882;48.8553323;20.8966344;0;0;0;0 +50.5;0;0;1;10;3;3;1;2;1;2;1;0;1100.04612;2699.99999;1;311.030713;68.575835;3;0.782120411;0;0;20899.62;0;49.4035099;13.7231972;0.3106941;262.09376;0;1;50.5;0;10;3;2;2;0.570407428;49.4146948;20.89962;0;0;0;0 +51;0;0;1;10;3;3;1;2;1;2;1;0;1112.50359;2699.60915;1;314.507445;69.3524216;3;0.83042158;0;0;20896.5946;0;49.9625767;13.8784935;0.310478992;265.059701;0;1;51;0;10;3;2;2;0.579986864;49.9737537;20.8965946;0;0;0;0 +51.5;0;0;1;10;3;3;1;2;1;2;1;0;1124.95065;2698.44366;1;317.888958;70.1283594;3;0.868014986;0;0;20887.573;0;50.5211656;14.0336571;0.310165242;268.023108;0;1;51.5;0;10;3;2;2;0.589674116;50.5323313;20.887573;0;0;0;0 +52;0;0;1;10;3;3;1;2;1;2;1;0;1137.38335;2696.50621;1;321.171432;70.903402;3;1;0;0;20872.576;0;51.0790995;14.1886387;0.309753243;270.983039;0;1;52;0;10;3;2;2;0.599469077;51.0902503;20.872576;0;0;0;0 +52.5;0;0;1;10;3;3;1;2;1;2;1;0;1149.79775;2693.80046;1;324.351191;71.6773045;3;1;0;0;20851.6318;0;51.636202;14.3433894;0.309243513;273.93856;0;1;52.5;0;10;3;2;2;0.609371604;51.6473344;20.8516318;0;0;0;0 +53;0;0;1;10;3;3;1;2;1;2;1;0;1162.18996;2690.33102;1;327.424715;72.4498232;3;1;0;0;20824.7763;0;52.1922978;14.4978605;0.30863669;276.888739;0;1;53;0;10;3;2;2;0.619381523;52.2034082;20.8247763;0;0;0;0 +53.5;0;0;1;10;3;3;1;2;1;2;1;0;1174.55609;2686.10347;1;330.38865;73.2207161;3;1;0;0;20792.0525;0;52.7472127;14.6520035;0.307933535;279.832655;0;1;53.5;0;10;3;2;2;0.629498623;52.7582978;20.7920525;0;0;0;0 +54;0;0;1;10;3;3;1;2;1;2;1;0;1186.8923;2681.12432;1;333.239813;73.9897434;3;1;0;0;20753.5109;0;53.3007742;14.8057706;0.307134928;282.769389;0;1;54;0;10;3;2;2;0.639722663;53.3118304;20.7535109;0;0;0;0 +54.5;0;0;1;10;3;3;1;2;1;2;1;0;1199.19476;2675.401;1;335.975204;74.7566674;3;1;0;0;20709.209;0;53.8528113;14.9591143;0.306241864;285.698037;0;1;54.5;0;10;3;2;2;0.650053366;53.8638353;20.709209;0;0;0;0 +55;0;0;1;10;3;3;1;2;1;2;1;0;1211.45972;2668.94183;1;338.59201;75.5212534;3;1;0;0;20659.2111;0;54.403155;15.1119875;0.305255453;288.617702;0;1;55;0;10;3;2;2;0.660490423;54.4141435;20.6592111;0;0;0;0 +55.5;0;0;1;10;3;3;1;2;1;2;1;0;1223.68345;2661.75601;1;341.087614;76.2832691;3;1;0;0;20603.5886;0;54.9516385;15.264344;0.304176917;291.527497;0;1;55.5;0;10;3;2;2;0.671033493;54.962588;20.6035886;0;0;0;0 +56;0;0;1;10;3;3;1;2;1;2;1;0;1235.86228;2653.8536;1;343.459598;77.0424859;3;1;0;0;20542.4192;0;55.498097;15.4161381;0.303007585;294.426549;0;1;56;0;10;3;2;2;0.681682203;55.5090044;20.5424192;0;0;0;0 +56.5;0;0;1;10;3;3;1;2;1;2;1;0;1247.9926;2645.24548;1;345.705754;77.7986782;3;1;0;0;20475.7871;0;56.0423685;15.5673246;0.301748891;297.313999;0;1;56.5;0;10;3;2;2;0.692436148;56.0532305;20.4757871;0;0;0;0 +57;0;0;1;10;3;3;1;2;1;2;1;0;1260.07084;2635.94332;1;347.82408;78.5516243;3;1;0;0;20403.7828;0;56.5842934;15.7178593;0.300402369;300.189;0;1;57;0;10;3;2;2;0.703294893;56.5951069;20.4037828;0;0;0;0 +57.5;0;0;1;10;3;3;1;2;1;2;1;0;1272.09351;2625.95956;1;349.812794;79.3011064;3;1;0;0;20326.5026;0;57.1237152;15.8676987;0.29896965;303.050722;0;1;57.5;0;10;3;2;2;0.71425797;57.1344771;20.3265026;0;0;0;0 +58;0;0;1;10;3;3;1;2;1;2;1;0;1284.05719;2615.30737;1;351.670326;80.0469111;3;1;0;0;20244.0483;0;57.6604803;16.0168001;0.297452458;305.898349;0;1;58;0;10;3;2;2;0.725324884;57.6711875;20.2440483;0;0;0;0 +58.5;0;0;1;10;3;3;1;2;1;2;1;0;1295.95853;2604.00064;1;353.395332;80.788829;3;1;0;0;20156.5273;0;58.1944384;16.1651218;0.295852607;308.731084;0;1;58.5;0;10;3;2;2;0.736495109;58.2050879;20.1565273;0;0;0;0 +59;0;0;1;10;3;3;1;2;1;2;1;0;1307.79423;2592.05389;1;354.986684;81.5266556;3;1;0;0;20064.0524;0;58.7254422;16.3126228;0.294171992;311.548147;0;1;59;0;10;3;2;2;0.747768091;58.7360312;20.0640524;0;0;0;0 +59.5;0;0;1;10;3;3;1;2;1;2;1;0;1319.5611;2579.4823;1;356.443481;82.2601913;3;1;0;0;19966.7407;0;59.2533483;16.4592634;0.292412588;314.348776;0;1;59.5;0;10;3;2;2;0.759143247;59.2638739;19.9667407;0;0;0;0 +60;0;0;1;10;3;3;1;2;1;2;1;0;1331.25602;2566.30163;1;357.765042;82.9892412;3;1;0;0;19864.7144;0;59.7780168;16.6050047;0.290576448;317.132229;0;1;60;0;10;3;2;2;0.770619968;59.7884762;19.8647144;0;0;0;0 +60.5;0;0;1;10;4;4;1;2;1;2;1;0;1342.87594;2552.5282;1;358.95091;83.713616;3;0.636330477;0;1;19758.0997;0;60.2993113;16.7498087;0.288665689;319.897782;0;1;60.5;0;10;4;2;2;0.782197617;60.3097019;19.7580997;0;0;0;0 +61;0;0;1;10;4;4;1;2;1;2;1;0;1423.23686;2438.79483;1;363.480458;88.7232398;3;0.404916476;1;1;15671.4824;0;60.7764069;16.8823352;0.224722045;322.428853;0;1;61;0;10;4;2;2;0.794043285;60.784414;15.6714824;0;0;0;0 +61.5;0;0;1;10;4;4;1;2;1;2;1;0;1458.54481;1302.49983;0.598257085;198.941812;54.3961092;3;0.257660694;1;1;9972.24185;0;61.0892073;16.9692242;0.131679481;324.08831;0;1;61.5;0;10;4;2;2;0.803298298;61.093895;9.97224185;0;0;0;0 +62;0;0;1;10;4;4;1;2;1;2;1;0;1469.67508;840.682698;0.429094113;129.384442;39.3128106;3;0.163957353;1;1;6345.64142;0;61.2678859;17.0188572;0.0724809031;325.03623;0;1;62;0;10;4;2;2;0.809733964;61.2704616;6.34564142;0;0;0;0 +62.5;0;0;1;10;4;4;1;2;1;2;1;0;1494.77747;379.718913;0.260391102;59.4384381;24.2640276;4;0.0198504994;0;0;548.767055;0;61.3521024;17.0422507;-0.0243501607;325.483012;0;1;62.5;0;10;4;2;2;0.814389682;61.351305;0.548767055;0;0;0;0 +63;0;0;1;10;4;4;1;2;1;2;1;0;1454.98086;1105.32037;0.523402349;168.412404;47.4737084;4;0.237137336;1;0;6555.66165;0;61.4014777;17.055966;0.0731812678;325.744956;0;1;63;0;10;4;2;2;0.818610497;61.4041738;6.55566165;0;0;0;0 +63.5;0;0;1;10;4;4;1;2;1;2;1;0;1433.2499;1993.06723;0.842308204;299.13862;75.2580843;4;0.406254409;1;0;11230.9031;0;61.6056904;17.1126918;0.149029436;326.828338;0;1;63.5;0;10;4;2;2;0.827331859;61.6111033;11.2309031;0;0;0;0 +64;0;0;1;10;4;4;1;2;1;2;1;0;1396.7786;2472.90959;0.997519049;361.713261;86.8578312;4;0.537880351;1;0;14869.7023;0;61.9303034;17.2028621;0.208000806;328.550464;0;1;64;0;10;4;2;2;0.838995896;61.9378287;14.8697023;0;0;0;0 +64.5;0;0;1;10;4;4;1;2;1;2;1;0;1215.06872;2666.86004;0.999987214;339.335803;75.7452666;4;0.640326474;1;0;17701.8254;0;62.348513;17.3190314;0.253836112;330.769135;0;1;64.5;0;10;4;2;2;0.850389592;62.3576801;17.7018254;0;0;0;0 +65;0;0;1;10;4;4;1;2;1;2;1;0;999.097665;2674.5466;0.999999934;279.825141;62.2827994;4;0.720061578;0;0;14787.5682;0;62.7975543;17.4437651;0.207378136;333.151373;0;1;65;0;10;4;2;2;0.859709157;62.80502;14.7875682;0;0;0;0 +65.5;0;0;1;10;4;4;1;2;1;2;1;0;1005.04017;2677.45658;1;281.795773;62.6532536;4;0.782120411;0;0;14803.6574;0;63.1709456;17.5474849;0.20749603;335.132275;0;1;65.5;0;10;4;2;2;0.868385784;63.1784156;14.8036574;0;0;0;0 +66;0;0;1;10;4;4;1;2;1;2;1;0;1010.98584;2680.1912;1;283.75235;63.023901;4;0.83042158;0;0;14818.7771;0;63.544534;17.6512594;0.207597336;337.114223;0;1;66;0;10;4;2;2;0.877113877;63.5520075;14.8187771;0;0;0;0 +66.5;0;0;1;10;4;4;1;2;1;2;1;0;1016.9342;2682.75018;1;285.694383;63.3947159;4;0.868014986;0;0;14832.9257;0;63.9182894;17.7550804;0.207682042;339.097057;0;1;66.5;0;10;4;2;2;0.885893461;63.925766;14.8329257;0;0;0;0 +67;0;0;1;10;4;4;1;2;1;2;1;0;1022.88476;2685.1331;1;287.621367;63.7656688;4;1;0;0;14846.1009;0;64.2921821;17.8589395;0.207750119;341.080619;0;1;67;0;10;4;2;2;0.894724557;64.2996611;14.8461009;0;0;0;0 +67.5;0;0;1;10;4;4;1;2;1;2;1;0;1028.83707;2687.33959;1;289.532801;64.1367298;4;1;0;0;14858.3006;0;64.666182;17.9628283;0.207801547;343.064751;0;1;67.5;0;10;4;2;2;0.903607182;64.6736629;14.8583006;0;0;0;0 +68;0;0;1;10;4;4;1;2;1;2;1;0;1034.79063;2689.36935;1;291.428188;64.5078693;4;1;0;0;14869.5231;0;65.0402592;18.0667387;0.207836308;345.049292;0;1;68;0;10;4;2;2;0.91254135;65.0477413;14.8695231;0;0;0;0 +68.5;0;0;1;10;4;4;1;2;1;2;1;0;1040.74498;2691.22211;1;293.307037;64.8790575;4;1;0;0;14879.767;0;65.4143837;18.1706621;0.20785439;347.034084;0;1;68.5;0;10;4;2;2;0.921527068;65.4218664;14.879767;0;0;0;0 +69;0;0;1;10;4;4;1;2;1;2;1;0;1046.69962;2692.89767;1;295.168859;65.2502645;4;1;0;0;14889.0312;0;65.7885254;18.2745904;0.207855788;349.018967;0;1;69;0;10;4;2;2;0.930564342;65.7960082;14.8890312;0;0;0;0 +69.5;0;0;1;10;4;4;1;2;1;2;1;0;1052.65409;2694.39591;1;297.013174;65.6214607;4;1;0;0;14897.315;0;66.1626542;18.3785151;0.207840501;351.003782;0;1;69.5;0;10;4;2;2;0.939653172;66.1701365;14.897315;0;0;0;0 +70;0;0;1;10;4;4;1;2;1;2;1;0;1058.60791;2695.71674;1;298.839506;65.9926161;4;1;0;0;14904.6178;0;66.5367403;18.4824279;0.207808534;352.98837;0;1;70;0;10;4;2;2;0.948793555;66.5442214;14.9046178;0;0;0;0 +70.5;0;0;1;10;4;4;1;2;1;2;1;0;1064.5606;2696.86012;1;300.647384;66.3637011;4;1;0;0;14910.9396;0;66.9107535;18.5863204;0.207759896;354.972572;0;1;70.5;0;10;4;2;2;0.957985482;66.9182328;14.9109396;0;0;0;0 +71;0;0;1;10;4;4;1;2;1;2;1;0;1070.51168;2697.8261;1;302.436345;66.7346858;4;1;0;0;14916.2805;0;67.2846639;18.6901844;0.207694603;356.956228;0;1;71;0;10;4;2;2;0.967228942;67.2921408;14.9162805;0;0;0;0 +71.5;0;0;1;10;4;4;1;2;1;2;1;0;1076.46068;2698.61475;1;304.205931;67.1055405;4;1;0;0;14920.641;0;67.6584414;18.7940115;0.207612675;358.93918;0;1;71.5;0;10;4;2;2;0.976523919;67.6659154;14.920641;0;0;0;0 +72;0;0;1;10;4;4;1;2;1;2;1;0;1082.40711;2699.22623;1;305.955694;67.4762356;4;1;0;0;14924.0218;0;68.0320563;18.8977934;0.207514139;360.921268;0;1;72;0;10;4;2;2;0.985870393;68.0395267;14.9240218;0;0;0;0 +72.5;0;0;1;10;4;4;1;2;1;2;1;0;1088.35051;2699.66072;1;307.685188;67.8467413;4;1;0;0;14926.4241;0;68.4054785;19.0015218;0.207399026;362.902335;0;1;72.5;0;10;4;2;2;0.995268339;68.4129448;14.9264241;0;0;0;0 +73;0;0;1;10;4;4;1;2;1;2;1;0;1094.29039;2699.9185;1;309.39398;68.2170282;4;1;0;0;14927.8494;0;68.7786784;19.1051884;0.207267373;364.882222;0;1;73;0;10;4;2;2;1.00471773;68.7861399;14.9278494;0;0;0;0 +73.5;0;0;1;10;4;4;1;2;1;2;1;0;1100.2263;2699.99987;1;311.081641;68.5870667;4;1;0;0;14928.2993;0;69.1516262;19.208785;0.207119221;366.860771;0;1;73.5;0;10;4;2;2;1.01421853;69.1590823;14.9282993;0;0;0;0 +74;0;0;1;10;4;4;1;2;1;2;1;0;1106.15774;2699.90521;1;312.747752;68.9568274;4;1;0;0;14927.7759;0;69.5242921;19.3123034;0.206954617;368.837825;0;1;74;0;10;4;2;2;1.02377071;69.5317423;14.9277759;0;0;0;0 +74.5;0;0;1;10;4;4;1;2;1;2;1;0;1112.08426;2699.63493;1;314.391901;69.3262809;4;1;0;0;14926.2815;0;69.8966467;19.4157352;0.206773614;370.813228;0;1;74.5;0;10;4;2;2;1.03337422;69.9040904;14.9262815;0;0;0;0 +75;0;0;1;10;4;4;1;2;1;2;1;0;1118.00538;2699.18952;1;316.013685;69.6953981;4;1;0;0;14923.8188;0;70.2686605;19.5190724;0.206576269;372.786822;0;1;75;0;10;4;2;2;1.04302902;70.2760971;14.9238188;0;0;0;0 +75.5;0;0;1;10;4;4;1;2;1;2;1;0;1123.92064;2698.56951;1;317.61271;70.0641497;4;1;0;0;14920.3908;0;70.6403041;19.6223067;0.206362644;374.758453;0;1;75.5;0;10;4;2;2;1.05273506;70.647733;14.9203908;0;0;0;0 +76;0;0;1;10;4;4;1;2;1;2;1;0;1129.82957;2697.77549;1;319.188591;70.4325067;4;1;0;0;14916.0007;0;71.0115483;19.7254301;0.206132808;376.727964;0;1;76;0;10;4;2;2;1.06249229;71.0189689;14.9160007;0;0;0;0 +76.5;0;0;1;10;4;4;1;2;1;2;1;0;1135.73171;2696.80811;1;320.740952;70.8004404;4;1;0;0;14910.6521;0;71.382364;19.8284344;0.205886834;378.695203;0;1;76.5;0;10;4;2;2;1.07230065;71.3897757;14.9106521;0;0;0;0 +77;0;0;1;10;4;4;1;2;1;2;1;0;1141.62659;2695.66807;1;322.269427;71.1679218;4;1;0;0;14904.3487;0;71.7527221;19.9313117;0.205624797;380.660014;0;1;77;0;10;4;2;2;1.08216008;71.7601244;14.9043487;0;0;0;0 +77.5;0;0;1;10;4;4;1;2;1;2;1;0;1147.51376;2694.35611;1;323.77366;71.5349224;4;1;0;0;14897.0949;0;72.1225939;20.0340539;0.205346783;382.622244;0;1;77.5;0;10;4;2;2;1.09207051;72.1299862;14.8970949;0;0;0;0 +78;0;0;1;10;4;4;1;2;1;2;1;0;1153.39276;2692.87303;1;325.253303;71.9014137;4;1;0;0;14888.895;0;72.4919507;20.136653;0.205052877;384.581743;0;1;78;0;10;4;2;2;1.10203188;72.4993324;14.888895;0;0;0;0 +78.5;0;0;1;10;4;4;1;2;1;2;1;0;1159.26314;2691.2197;1;326.708021;72.2673675;4;1;0;0;14879.7537;0;72.8607639;20.2391011;0.204743172;386.538358;0;1;78.5;0;10;4;2;2;1.11204412;72.8681345;14.8797537;0;0;0;0 +79;0;0;1;10;4;4;1;2;1;2;1;0;1165.12444;2689.39702;1;328.137487;72.6327555;4;1;0;0;14869.6761;0;73.2290053;20.3413904;0.204417765;388.491939;0;1;79;0;10;4;2;2;1.12210714;73.2363641;14.8696761;0;0;0;0 +79.5;0;0;1;10;4;4;1;2;1;2;1;0;1170.97622;2687.40594;1;329.541387;72.9975498;4;1;0;0;14858.6675;0;73.5966465;20.4435129;0.204076759;390.442336;0;1;79.5;0;10;4;2;2;1.13222087;73.6039931;14.8586675;0;0;0;0 +80;0;0;1;10;4;4;1;2;1;2;1;0;1176.81802;2685.24748;1;330.919414;73.3617226;4;1;0;0;14846.7333;0;73.9636598;20.545461;0.203720259;392.389402;0;1;80;0;10;4;2;2;1.14238522;73.9709934;14.8467333;0;0;0;0 +80.5;0;0;1;10;4;4;1;2;1;2;1;0;1182.64942;2682.92268;1;332.271276;73.7252464;4;1;0;0;14833.8795;0;74.3300171;20.647227;0.203348377;394.332988;0;1;80.5;0;10;4;2;2;1.15260011;74.3373374;14.8338795;0;0;0;0 +81;0;0;1;10;4;4;1;2;1;2;1;0;1188.46997;2680.43266;1;333.596689;74.0880938;4;1;0;0;14820.1122;0;74.6956911;20.7488031;0.202961227;396.272949;0;1;81;0;10;4;2;2;1.16286543;74.7029974;14.8201122;0;0;0;0 +81.5;0;0;1;10;4;4;1;2;1;2;1;0;1194.27923;2677.77857;1;334.895381;74.4502376;4;1;0;0;14805.4377;0;75.0606542;20.8501817;0.20255893;398.209138;0;1;81.5;0;10;4;2;2;1.17318111;75.0679461;14.8054377;0;0;0;0 +82;0;0;1;10;4;4;1;2;1;2;1;0;1200.07677;2674.9616;1;336.167093;74.8116508;4;1;0;0;14789.8627;0;75.4248795;20.9513554;0.20214161;400.141413;0;1;82;0;10;4;2;2;1.18354703;75.4321563;14.7898627;0;0;0;0 +82.5;0;0;1;10;4;4;1;2;1;2;1;0;1205.86216;2671.98301;1;337.411576;75.1723068;4;1;0;0;14773.394;0;75.7883398;21.0523166;0.201709396;402.06963;0;1;82.5;0;10;4;2;2;1.19396309;75.795601;14.773394;0;0;0;0 +83;0;0;1;10;4;4;1;2;1;2;1;0;1211.63498;2668.84408;1;338.628591;75.532179;4;1;0;0;14756.0389;0;76.1510086;21.1530579;0.20126242;403.993648;0;1;83;0;10;4;2;2;1.20442919;76.1582537;14.7560389;0;0;0;0 +83.5;0;0;1;10;4;4;1;2;1;2;1;0;1217.39481;2665.54615;1;339.817914;75.8912411;4;1;0;0;14737.8046;0;76.5128594;21.2535721;0.200800819;405.913326;0;1;83.5;0;10;4;2;2;1.21494521;76.5200879;14.7378046;0;0;0;0 +84;0;0;1;10;4;4;1;2;1;2;1;0;1223.14123;2662.0906;1;340.979331;76.2494673;4;1;0;0;14718.6989;0;76.873866;21.3538517;0.200324733;407.828526;0;1;84;0;10;4;2;2;1.22551105;76.8810773;14.7186989;0;0;0;0 +84.5;0;0;1;10;4;4;1;2;1;2;1;0;1228.87382;2658.47885;1;342.112639;76.6068317;4;1;0;0;14698.7295;0;77.2340024;21.4538896;0.199834308;409.739109;0;1;84.5;0;10;4;2;2;1.23612657;77.2411961;14.6987295;0;0;0;0 +85;0;0;1;10;4;4;1;2;1;2;1;0;1234.59218;2654.71236;1;343.217649;76.9633088;4;1;0;0;14677.9047;0;77.5932431;21.5536786;0.199329692;411.644939;0;1;85;0;10;4;2;2;1.24679167;77.6004186;14.6779047;0;0;0;0 +85.5;0;0;0;0;4;4;1;2;1;2;1;0;1234.69511;-284.772434;0.00515377521;-36.8202128;0.396684664;4;1;0;0;-1574.50679;0;77.5713826;21.5476063;-0.063130258;411.528966;0;1;85.5;0;0;4;2;2;1.24870681;77.5691045;-1.57450679;0;0;0;0 +86;0;0;0;0;4;4;1;2;1;2;1;0;1232.8546;-299.921488;2.65613989e-05;-38.7211332;0.00204137604;4;1;0;0;-1658.26591;0;77.4555643;21.5154345;-0.064579681;410.914531;0;1;86;0;0;4;2;2;1.24871667;77.4532395;-1.65826591;0;0;0;0 +86.5;0;0;0;0;4;4;1;2;1;2;1;0;1231.00506;-299.999595;1.36891479e-07;-38.6731124;1.05050098e-05;4;1;0;0;-1658.69776;0;77.3393594;21.4831554;-0.0645329684;410.298045;0;1;86.5;0;0;4;2;2;1.24871672;77.3370363;-1.65869776;0;0;0;0 +87;0;0;0;0;4;4;1;2;1;2;1;0;1229.15703;-299.999998;7.05507911e-10;-38.6151066;5.40591814e-08;4;1;0;0;-1658.69999;0;77.2232499;21.4509028;-0.0644786688;409.682065;0;1;87;0;0;4;2;2;1.24871672;77.2209287;-1.65869999;0;0;0;0 +87.5;0;0;0;0;4;4;1;2;1;2;1;0;1227.31055;-300;3.63602918e-12;-38.557098;2.78190334e-10;4;1;0;0;-1658.7;0;77.1072381;21.4186772;-0.0644244566;409.066603;0;1;87.5;0;0;4;2;2;1.24871672;77.1049188;-1.6587;0;0;0;0 +88;0;0;0;0;4;4;1;2;1;2;1;0;1225.46562;-300;1.8739277e-14;-38.4991379;1.43157522e-12;4;1;0;0;-1658.7;0;76.9913237;21.3864788;-0.0643703712;408.451658;0;1;88;0;0;4;2;2;1.24871672;76.9890064;-1.6587;0;0;0;0 +88.5;0;0;0;0;4;4;1;2;1;2;1;0;1223.62224;-300;9.65780214e-17;-38.4412265;7.36691867e-15;4;1;0;0;-1658.7;0;76.8755066;21.3543074;-0.0643164124;407.837229;0;1;88.5;0;0;4;2;2;1.24871672;76.8731913;-1.6587;0;0;0;0 +89;0;0;0;0;4;4;1;2;1;2;1;0;1221.78041;-300;4.97741412e-19;-38.3833635;3.7910293e-17;4;1;0;0;-1658.7;0;76.7597865;21.3221629;-0.06426258;407.223315;0;1;89;0;0;4;2;2;1.24871672;76.7574731;-1.6587;0;0;0;0 +89.5;0;0;0;0;4;4;1;2;1;2;1;0;1219.94011;-300;2.56524735e-21;-38.3255489;1.95086837e-19;4;1;0;0;-1658.7;0;76.6441632;21.2900453;-0.0642088735;406.609914;0;1;89.5;0;0;4;2;2;1.24871672;76.6418517;-1.6587;0;0;0;0 +90;0;0;0;0;4;4;1;2;1;2;1;0;1218.10135;-300;1.32207082e-23;-38.2677827;1.00391826e-21;4;1;0;0;-1658.7;0;76.5286364;21.2579546;-0.0641552928;405.997026;0;1;90;0;0;4;2;2;1.24871672;76.5263269;-1.6587;0;0;0;0 +90.5;0;0;0;0;4;4;1;2;1;2;1;0;1216.26413;-300;6.81365581e-26;-38.2100645;5.16616532e-24;4;1;0;0;-1658.7;0;76.413206;21.2258906;-0.0641018375;405.384648;0;1;90.5;0;0;4;2;2;1.24871672;76.4108984;-1.6587;0;0;0;0 +91;0;0;0;0;4;4;1;2;1;2;1;0;1214.42844;-300;3.51160504e-28;-38.1523945;2.65850695e-26;4;1;0;0;-1658.7;0;76.2978717;21.1938532;-0.0640485073;404.772781;0;1;91;0;0;4;2;2;1.24871672;76.2955659;-1.6587;0;0;0;0 +91.5;0;0;0;0;4;4;1;2;1;2;1;0;1212.59427;-300;1.8098023e-30;-38.0947724;1.36806539e-28;4;1;0;0;-1658.7;0;76.1826332;21.1618426;-0.0639953018;404.161422;0;1;91.5;0;0;4;2;2;1.24871672;76.1803294;-1.6587;0;0;0;0 +92;0;0;0;0;4;4;1;2;1;2;1;0;1210.76162;-300;9.32731422e-33;-38.0371981;7.04004545e-31;4;1;0;0;-1658.7;0;76.0674904;21.1298584;-0.0639422208;403.55057;0;1;92;0;0;4;2;2;1.24871672;76.0651885;-1.6587;0;0;0;0 +92.5;0;0;0;0;4;4;1;2;1;2;1;0;1208.93049;-300;4.80708808e-35;-37.9796716;3.62279384e-33;4;1;0;0;-1658.7;0;75.952443;21.0979008;-0.0638892641;402.940225;0;1;92.5;0;0;4;2;2;1.24871672;75.9501431;-1.6587;0;0;0;0 +93;0;0;0;0;4;4;1;2;1;2;1;0;1207.10088;-300;2.47746514e-37;-37.9221927;1.86428081e-35;4;1;0;0;-1658.7;0;75.8374909;21.0659697;-0.0638364312;402.330385;0;1;93;0;0;4;2;2;1.24871672;75.8351928;-1.6587;0;0;0;0 +93.5;0;0;0;0;4;4;1;2;1;2;1;0;1205.27278;-300;1.27682984e-39;-37.8647612;9.59353319e-38;4;1;0;0;-1658.7;0;75.7226337;21.0340649;-0.0637837219;401.721049;0;1;93.5;0;0;4;2;2;1.24871672;75.7203375;-1.6587;0;0;0;0 +94;0;0;0;0;4;4;1;2;1;2;1;0;1203.44619;-300;6.58049397e-42;-37.8073772;4.93679828e-40;4;1;0;0;-1658.7;0;75.6078713;21.0021865;-0.0637311358;401.112215;0;1;94;0;0;4;2;2;1.24871672;75.605577;-1.6587;0;0;0;0 +94.5;0;0;0;0;4;4;1;2;1;2;1;0;1201.62111;-300;3.39143867e-44;-37.7500404;2.54045628e-42;4;1;0;0;-1658.7;0;75.4932034;20.9703343;-0.0636786728;400.503883;0;1;94.5;0;0;4;2;2;1.24871672;75.490911;-1.6587;0;0;0;0 +95;0;0;0;0;4;4;1;2;1;2;1;0;1199.79752;-300;1.74787125e-46;-37.6927508;1.30730707e-44;4;1;0;0;-1658.7;0;75.3786299;20.9385083;-0.0636263324;399.896052;0;1;95;0;0;4;2;2;1.24871672;75.3763394;-1.6587;0;0;0;0 +95.5;0;0;0;0;0;0;1;2;1;2;1;0;1197.97544;-300;9.00813552e-49;-37.6355083;6.72733467e-47;4;0.636330477;0;1;-1658.7;0;75.2641505;20.9067085;-0.0635741144;399.288719;0;1;95.5;0;0;0;2;2;1.24871672;75.2618618;-1.6587;0;0;0;0 +96;0;0;0;0;0;0;1;2;1;2;1;0;1196.15484;-300;4.64259055e-51;-37.5783127;3.461848e-49;4;0.404916476;0;1;-1658.7;0;75.1497649;20.8749347;-0.0635220185;398.681885;0;1;96;0;0;0;2;2;1.24871672;75.1474781;-1.6587;0;0;0;0 +96.5;0;0;0;0;0;0;1;2;1;2;1;0;1194.33574;-300;2.39268681e-53;-37.5211639;1.78144531e-51;4;0.257660694;0;1;-1658.7;0;75.035473;20.8431869;-0.0634700444;398.075548;0;1;96.5;0;0;0;2;2;1.24871672;75.0331881;-1.6587;0;0;0;0 +97;0;0;0;0;0;0;1;2;1;2;1;0;1192.51813;-300;1.233137e-55;-37.4640619;9.16719619e-54;4;0.163957353;0;1;-1658.7;0;74.9212745;20.8114651;-0.0634181918;397.469706;0;1;97;0;0;0;2;2;1.24871672;74.9189915;-1.6587;0;0;0;0 +97.5;0;0;0;0;0;0;1;2;1;2;1;0;1179.38808;-300;6.35531087e-58;-37.0515692;4.67254768e-56;0;0.0198504994;0;0;0;0;74.8110286;20.7808413;-0.03656613;396.884833;0;1;97.5;0;0;0;2;2;1.24871672;74.8097123;0;0;0;0;0 +98;0;0;0;0;0;0;1;2;1;2;1;0;1036.14863;-300;3.27538436e-60;-32.5515692;2.11565348e-58;0;0.237137336;0;0;0;0;74.745237;20.7625658;-0.036536337;396.535798;0;1;98;0;0;0;2;2;1.24871672;74.7439217;0;0;0;0;0 +98.5;0;0;0;0;0;0;1;2;1;2;1;0;892.90918;-300;1.68805947e-62;-28.0515692;9.39626465e-61;0;0.406254409;0;0;0;0;74.6794989;20.7443052;-0.0365065944;396.187046;0;1;98.5;0;0;0;2;2;1.24871672;74.6781846;0;0;0;0;0 +99;0;0;0;0;0;0;1;2;1;2;1;0;749.669731;-300;8.69987905e-65;-23.5515692;4.06577555e-63;0;0.537880351;0;0;0;0;74.6138143;20.7260595;-0.0364769022;395.838579;0;1;99;0;0;0;2;2;1.24871672;74.6125011;0;0;0;0;0 +99.5;0;0;0;0;0;0;1;2;1;2;1;0;606.430283;-300;4.4837221e-67;-19.0515692;1.69503932e-65;0;0.640326474;0;0;0;0;74.548183;20.7078286;-0.0364472602;395.490394;0;1;99.5;0;0;0;2;2;1.24871672;74.546871;0;0;0;0;0 +100;0;0;0;0;0;0;1;2;1;2;1;0;589.06546;-0.0219585322;0.127793537;-0.00135455138;4.6928066;0;0.720061578;0;0;0;0;74.4826052;20.6896125;-0.0364176684;395.142493;0;1;100;0;0;0;2;2;1.24930245;74.4812941;0;0;0;0;0 +100.5;0;0;0;0;0;0;1;2;1;2;1;0;590.454633;1.68858461;0.128328494;0.10440901;4.72356439;0;0.782120411;0;0;0;0;74.4170805;20.6714112;-0.0363881265;394.794874;0;1;100.5;0;0;0;2;2;1.24996118;74.4157705;0;0;0;0;0 +101;0;0;0;0;0;0;1;2;1;2;1;0;590.434907;-0.14900957;0.127549568;-0.00921329133;4.69473654;0;0.83042158;0;0;0;0;74.3516089;20.6532247;-0.0363586346;394.447536;0;1;101;0;0;0;2;2;1.25061338;74.3503;0;0;0;0;0 +101.5;0;0;0;0;0;0;1;2;1;2;1;0;590.428288;0.00300752205;0.127615148;0.000185953602;4.69709769;0;0.868014986;0;0;0;0;74.2861904;20.6350529;-0.0363291925;394.10048;0;1;101.5;0;0;0;2;2;1.25126573;74.2848826;0;0;0;0;0 +102;0;0;0;0;0;0;1;2;1;2;1;0;590.428991;0.000630927026;0.127614039;3.90099526e-05;4.6970625;0;1;0;0;0;0;74.2208249;20.6168958;-0.0362998002;393.753705;0;1;102;0;0;0;2;2;1.2519181;74.2195181;0;0;0;0;0 +102.5;0;0;0;0;0;0;1;2;1;2;1;0;590.428969;-7.38564196e-05;0.127613743;-4.56651118e-06;4.6970514;0;1;0;0;0;0;74.1555122;20.5987534;-0.0362704574;393.407211;0;1;102.5;0;0;0;2;2;1.25257047;74.1542065;0;0;0;0;0 +103;0;0;0;0;0;0;1;2;1;2;1;0;590.428967;2.73015411e-06;0.127613776;1.68804273e-07;4.69705259;0;1;0;0;0;0;74.0902522;20.5806256;-0.0362411642;393.060996;0;1;103;0;0;0;2;2;1.25322284;74.0889476;0;0;0;0;0 +103.5;0;0;0;0;0;0;1;2;1;2;1;0;590.428967;2.03184015e-07;0.127613774;1.25627817e-08;4.69705255;0;1;0;0;0;0;74.025045;20.5625125;-0.0362119203;392.715061;0;1;103.5;0;0;0;2;2;1.25387521;74.0237414;0;0;0;0;0 +104;0;0;0;0;0;0;1;2;1;2;1;0;590.428967;-3.43870283e-08;0.127613774;-2.12613541e-09;4.69705255;0;1;0;0;0;0;73.9598904;20.544414;-0.0361827258;392.369405;0;1;104;0;0;0;2;2;1.25452757;73.9585878;0;0;0;0;0 +104.5;0;0;0;0;0;0;1;2;1;2;1;0;590.428967;1.81574933e-09;0.127613774;1.12267013e-10;4.69705255;0;1;0;0;0;0;73.8947882;20.5263301;-0.0361535805;392.024027;0;1;104.5;0;0;0;2;2;1.25517994;73.8934867;0;0;0;0;0 +105;0;0;0;0;0;0;1;2;1;2;1;0;590.428967;4.71800377e-11;0.127613774;2.91712176e-12;4.69705255;0;1;0;0;0;0;73.8297385;20.5082607;-0.0361244842;391.678928;0;1;105;0;0;0;2;2;1.25583231;73.828438;0;0;0;0;0 +105.5;0;0;0;0;0;0;1;2;1;2;1;0;590.428967;-1.4381385e-11;0.127613774;-8.89194947e-13;4.69705255;0;1;0;0;0;0;73.7647411;20.4902059;-0.036095437;391.334106;0;1;105.5;0;0;0;2;2;1.25648468;73.7634417;0;0;0;0;0 +106;0;0;0;0;0;0;1;2;1;2;1;0;590.428967;-2.84217094e-12;0.127613774;-1.75730227e-13;4.69705255;0;1;0;0;0;0;73.6997959;20.4721655;-0.0360664387;390.989561;0;1;106;0;0;0;2;2;1.25713705;73.6984976;0;0;0;0;0 diff --git a/tools/vehicle_graph/tests/diesel-mech.script b/tools/vehicle_graph/tests/diesel-mech.script new file mode 100644 index 00000000..c9a512f3 --- /dev/null +++ b/tools/vehicle_graph/tests/diesel-mech.script @@ -0,0 +1,18 @@ +# events for: -graph-run diesel-mech.vhg -script diesel-mech.script +# options: -every 50 +# start the engine and wait at idle; raise the speed, select the first gear and drive off with the clutch half engaged, +# engage it fully, shift up through the gears, then throttle back +# (engine braking) and shift to neutral (coasting, the engine back at idle) +1.0 press starter +1.5 release starter +9.0 value throttle 0.5 +10.0 value pedal 0.5 +10.0 set gear 1 +25.0 value pedal 1 +25.0 value throttle 1 +30.0 set gear 2 +45.0 set gear 3 +60.0 set gear 4 +85.0 value throttle 0 +95.0 set gear 0 +105.0 value throttle 0 diff --git a/tools/vehicle_graph/tests/diesel-mech.vhg b/tools/vehicle_graph/tests/diesel-mech.vhg new file mode 100644 index 00000000..7216f9cb --- /dev/null +++ b/tools/vehicle_graph/tests/diesel-mech.vhg @@ -0,0 +1,32 @@ +// expect: ok +// diesel locomotive with a mechanical transmission: engine, friction clutch with a four speed gearbox, final drive. +// start the engine, raise the speed, select the first gear and drive off with the clutch half engaged (slipping), then +// engage it fully and shift up; the gearbox releases and engages the clutch itself for the shifts +{ + "format": "maszyna-vehicle-graph", + "version": 2, + "vehicle": { "name": "diesel mechanical test" }, + "body": { "mass": 60000, "length": 14, "wheel_diameter": 1.0, "axle_arrangement": "B'B'", "power": 400, "vmax": 100, "bearing": "roll" }, + "nodes": [ + { "id": "starter", "type": "cab.switch", "params": { "behaviour": "monostable" } }, + { "id": "throttle", "type": "cab.switch", "params": { "behaviour": "multi", "positions": 11, "min": 0, "max": 1, "default": 0 } }, + { "id": "gear", "type": "cab.switch", "params": { "behaviour": "multi", "positions": 5, "min": 0, "max": 4, "default": 0 } }, + { "id": "pedal", "type": "cab.switch", "params": { "behaviour": "multi", "positions": 3, "min": 0, "max": 1, "default": 1 } }, + { "id": "reverser", "type": "cab.switch", "params": { "behaviour": "multi", "positions": 3, "min": -1, "max": 1, "default": 1 } }, + { "id": "engine", "type": "drive.diesel", "params": { "idle_rpm": 600, "max_rpm": 1500, "m_max": 3000, "rpm_m_max": 1100, "m_nmax": 2600 } }, + { "id": "box", "type": "drive.gearbox", "params": { "gears": [[1, 3.0], [2, 2.0], [3, 1.4], [4, 1.0]], "clutch_torque": 5000, "inertia": 10 } }, + { "id": "final", "type": "drive.gear", "params": { "ratio": 3.0, "eff": 0.97 } }, + { "id": "axles", "type": "drive.axle", "params": { "powered_axles": 1 } }, + { "id": "vs", "type": "veh.state" } + ], + "connections": [ + { "from": "starter.pos", "to": "engine.start" }, + { "from": "throttle.pos", "to": "engine.demand" }, + { "from": "gear.pos", "to": "box.gear" }, + { "from": "reverser.pos", "to": "box.dir" }, + { "from": "pedal.pos", "to": "box.clutch" }, + { "from": "engine.shaft", "to": "box.in" }, + { "from": "box.out", "to": "final.in" }, + { "from": "final.out", "to": "axles.shaft" } + ] +} diff --git a/tools/vehicle_graph/tests/lua-bad-port.vhg b/tools/vehicle_graph/tests/lua-bad-port.vhg new file mode 100644 index 00000000..be8d2561 --- /dev/null +++ b/tools/vehicle_graph/tests/lua-bad-port.vhg @@ -0,0 +1,2 @@ +// expect: error unknown type "double" +{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "b", "type": "lua.block", "lua": "block \"x\" { inputs = { a = \"double\" }, update = function(self, i, o, dt) end }\n" } ] } diff --git a/tools/vehicle_graph/tests/lua-load-loop.vhg b/tools/vehicle_graph/tests/lua-load-loop.vhg new file mode 100644 index 00000000..587f39d4 --- /dev/null +++ b/tools/vehicle_graph/tests/lua-load-loop.vhg @@ -0,0 +1,2 @@ +// expect: error instruction budget exceeded +{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "b", "type": "lua.block", "lua": "while true do end\n" } ] } diff --git a/tools/vehicle_graph/tests/lua-no-block.vhg b/tools/vehicle_graph/tests/lua-no-block.vhg new file mode 100644 index 00000000..9353f9c6 --- /dev/null +++ b/tools/vehicle_graph/tests/lua-no-block.vhg @@ -0,0 +1,2 @@ +// expect: error doesn't call block +{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "b", "type": "lua.block", "lua": "local x = 1\n" } ] } diff --git a/tools/vehicle_graph/tests/lua-no-update.vhg b/tools/vehicle_graph/tests/lua-no-update.vhg new file mode 100644 index 00000000..7de453d5 --- /dev/null +++ b/tools/vehicle_graph/tests/lua-no-update.vhg @@ -0,0 +1,2 @@ +// expect: error has no update +{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "b", "type": "lua.block", "lua": "block \"x\" { inputs = { a = \"bool\" } }\n" } ] } diff --git a/tools/vehicle_graph/tests/lua-port-twice.vhg b/tools/vehicle_graph/tests/lua-port-twice.vhg new file mode 100644 index 00000000..801227f1 --- /dev/null +++ b/tools/vehicle_graph/tests/lua-port-twice.vhg @@ -0,0 +1,2 @@ +// expect: error is both an input and an output +{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "b", "type": "lua.block", "lua": "block \"x\" { inputs = { a = \"bool\" }, outputs = { a = \"bool\" }, update = function(self, i, o, dt) end }\n" } ] } diff --git a/tools/vehicle_graph/tests/lua-sandbox.vhg b/tools/vehicle_graph/tests/lua-sandbox.vhg new file mode 100644 index 00000000..015a210d --- /dev/null +++ b/tools/vehicle_graph/tests/lua-sandbox.vhg @@ -0,0 +1,2 @@ +// expect: error attempt to index +{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "b", "type": "lua.block", "lua": "local f = io.open(\"file.txt\")\nblock \"x\" { update = function(self, i, o, dt) end }\n" } ] } diff --git a/tools/vehicle_graph/tests/lua-syntax.vhg b/tools/vehicle_graph/tests/lua-syntax.vhg new file mode 100644 index 00000000..f3bec8bc --- /dev/null +++ b/tools/vehicle_graph/tests/lua-syntax.vhg @@ -0,0 +1,2 @@ +// expect: error lua: b: +{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "b", "type": "lua.block", "lua": "block \"x\" { update = function(self, i, o, dt) o.q = end }\n" } ] } diff --git a/tools/vehicle_graph/tests/lua-unknown-param.vhg b/tools/vehicle_graph/tests/lua-unknown-param.vhg new file mode 100644 index 00000000..58ef9e95 --- /dev/null +++ b/tools/vehicle_graph/tests/lua-unknown-param.vhg @@ -0,0 +1,2 @@ +// expect: warning unknown parameter "lua.missing" +{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "b", "type": "lua.block", "params": { "lua.missing": 2 }, "lua": "block \"x\" { params = { k = 1 }, update = function(self, i, o, dt) end }\n" } ] } diff --git a/tools/vehicle_graph/tests/lua.expected.csv b/tools/vehicle_graph/tests/lua.expected.csv new file mode 100644 index 00000000..880ca65b --- /dev/null +++ b/tools/vehicle_graph/tests/lua.expected.csv @@ -0,0 +1,41 @@ +time;meddler.ok;probe.acc;probe.count;probe.kept;probe.q;probe.r;probe.y;looper.alive;a_sw.pos;a_sw.index;n_sw.pos;n_sw.index;go_sw.pos;go_sw.index;x.q;meddler:fault;probe:fault;looper:fault;a_sw:position;n_sw:position;go_sw:position +0.1;1;0.1;1;7;1;6.33587065;0;10;1;1;2;2;0;0;0;0;0;0;1;2;0 +0.2;1;0.2;2;7;1;2.56979865;0;20;1;1;2;2;0;0;0;0;0;0;1;2;0 +0.3;1;0.3;3;7;1;3.44937472;0;30;1;1;2;2;0;0;0;0;0;0;1;2;0 +0.4;1;0.4;4;7;1;4.09793284;0;40;1;1;2;2;0;0;0;0;0;0;1;2;0 +0.5;1;0.5;5;7;1;5.31127189;0;50;1;1;2;2;0;0;0;0;0;0;1;2;0 +0.6;1;0.6;6;7;1;6.79183938;4.5;60;1;1;2;2;0;0;1.5;0;0;0;1;2;0 +0.7;1;0.7;7;7;1;6.59821717;4.5;70;1;1;2;2;0;0;1.5;0;0;0;1;2;0 +0.8;1;0.8;8;7;1;5.79930266;4.5;80;1;1;2;2;0;0;1.5;0;0;0;1;2;0 +0.9;1;0.9;9;7;1;2.39093205;4.5;90;1;1;2;2;0;0;1.5;0;0;0;1;2;0 +1;1;1;10;7;1;6.08527614;4.5;100;1;1;2;2;0;0;1.5;0;0;0;1;2;0 +1.1;1;1.1;11;7;1;6.08029196;4.5;110;1;1;2;2;0;0;1.5;0;0;0;1;2;0 +1.2;1;1.2;12;7;1;6.7373392;4.5;120;1;1;2;2;0;0;1.5;0;0;0;1;2;0 +1.3;1;1.3;13;7;0;1.10218784;4.5;130;1;1;1;1;0;0;1.5;0;0;0;1;1;0 +1.4;1;1.4;14;7;0;2.64525326;4.5;140;1;1;1;1;0;0;1.5;0;0;0;1;1;0 +1.5;1;1.5;15;7;0;2.81758173;4.5;150;1;1;1;1;0;0;1.5;0;0;0;1;1;0 +1.6;1;1.6;16;7;0;3.02725761;4.5;150;1;1;1;1;1;1;1.5;0;0;1;1;1;1 +1.7;1;1.7;17;7;0;6.02885117;4.5;150;1;1;1;1;1;1;1.5;0;0;1;1;1;1 +1.8;1;1.8;18;7;0;4.82206049;4.5;150;1;1;1;1;1;1;1.5;0;0;1;1;1;1 +1.9;1;1.9;19;7;0;1.6949481;4.5;150;1;1;1;1;1;1;1.5;0;0;1;1;1;1 +2;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;1.5;0;0;1;1;1;1 +2.1;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;-1;0;1;1;1;1;1 +2.2;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;-1;0;1;1;1;1;1 +2.3;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;-1;0;1;1;1;1;1 +2.4;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;-1;0;1;1;1;1;1 +2.5;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;-1;0;1;1;1;1;1 +2.6;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;-1;0;1;1;1;1;1 +2.7;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;-1;0;1;1;1;1;1 +2.8;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;-1;0;1;1;1;1;1 +2.9;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;-1;0;1;1;1;1;1 +3;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;-1;0;1;1;1;1;1 +3.1;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;0;0;1;1;1;1;1 +3.2;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;0;0;1;1;1;1;1 +3.3;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;0;0;1;1;1;1;1 +3.4;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;0;0;1;1;1;1;1 +3.5;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;0;0;1;1;1;1;1 +3.6;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;0;0;1;1;1;1;1 +3.7;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;0;0;1;1;1;1;1 +3.8;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;0;0;1;1;1;1;1 +3.9;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;0;0;1;1;1;1;1 +4;1;2;20;7;0;3.08948651;4.5;150;1;1;1;1;1;1;0;0;1;1;1;1;1 diff --git a/tools/vehicle_graph/tests/lua.script b/tools/vehicle_graph/tests/lua.script new file mode 100644 index 00000000..8d02c3df --- /dev/null +++ b/tools/vehicle_graph/tests/lua.script @@ -0,0 +1,8 @@ +# events for: -graph-run lua.vhg -script lua.script +0.0 toggle a_sw +0.0 set n_sw 2 +0.5 force x.q 1.5 +1.2 set n_sw 1 +1.5 toggle go_sw +2.0 force x.q -1 +3.0 unforce x.q diff --git a/tools/vehicle_graph/tests/lua.vhg b/tools/vehicle_graph/tests/lua.vhg new file mode 100644 index 00000000..551e5221 --- /dev/null +++ b/tools/vehicle_graph/tests/lua.vhg @@ -0,0 +1,21 @@ +// expect: ok +// lua blocks: ports and parameters from the script, rate, state, deterministic random, sandbox, isolation of the blocks, +// a runtime error and an endless loop stopping only their own blocks; see lua.script +{ + "format": "maszyna-vehicle-graph", "version": 2, + "nodes": [ + { "id": "meddler", "type": "lua.block", "lua": "-- tries to leak into the other blocks of the vehicle\nshared = 1\nmath.floor = nil\nstring.upper = nil\nblock \"meddler\" {\n outputs = { ok = \"bool\" },\n update = function(self, i, o, dt) o.ok = true end,\n}\n" }, + { "id": "probe", "type": "lua.block", "params": { "lua.gain": 3 }, "lua": "-- the environment of this block is its own, and the sandbox has no files, system or code loading\nassert(shared == nil, \"globals leak between blocks\")\nassert(math.floor ~= nil and string.upper ~= nil, \"libraries leak between blocks\")\nassert(io == nil and os == nil and require == nil and loadstring == nil and dofile == nil and load == nil and debug == nil, \"sandbox\")\nblock \"probe\" {\n inputs = { a = \"bool\", n = \"int\", x = \"float\" },\n outputs = { q = \"bool\", count = \"int\", y = \"float\", r = \"float\", acc = \"float\", kept = \"float\" },\n params = { gain = 2.0, label = \"text\" },\n state = { count = 0, acc = 0.0 },\n rate = 10,\n update = function(self, i, o, dt)\n self.count = self.count + 1\n self.acc = self.acc + dt\n o.q = i.a and i.n >= 2\n o.count = self.count + 0.4\n o.y = i.x * self.gain\n o.r = math.random(1, 6) + math.random()\n o.acc = self.acc\n if self.count == 1 then o.kept = 7 end\n if self.count == 3 then print(\"third update, label\", self.label) end\n if i.x < 0 then error(\"negative input\") end\n end,\n}\n" }, + { "id": "looper", "type": "lua.block", "lua": "block \"looper\" {\n inputs = { go = \"bool\" },\n outputs = { alive = \"float\" },\n state = { n = 0 },\n update = function(self, i, o, dt)\n self.n = self.n + 1\n o.alive = self.n\n while i.go do end\n end,\n}\n" }, + { "id": "a_sw", "type": "cab.switch" }, + { "id": "n_sw", "type": "cab.switch", "params": { "behaviour": "multi", "positions": 4, "min": 0, "max": 3 } }, + { "id": "go_sw", "type": "cab.switch" }, + { "id": "x", "type": "logic.const", "params": { "value": 0 } } + ], + "connections": [ + { "from": "a_sw.pos", "to": "probe.a" }, + { "from": "n_sw.index", "to": "probe.n" }, + { "from": "x.q", "to": "probe.x" }, + { "from": "go_sw.pos", "to": "looper.go" } + ] +} diff --git a/tools/vehicle_graph/tests/module-loop.vhg b/tools/vehicle_graph/tests/module-loop.vhg new file mode 100644 index 00000000..088ddf97 --- /dev/null +++ b/tools/vehicle_graph/tests/module-loop.vhg @@ -0,0 +1,5 @@ +// expect: error form a loop +// a loop through a module needs a deferred block too, as in the graph itself +{ "format": "maszyna-vehicle-graph", "version": 2, + "nodes": [ { "id": "m", "type": "module", "params": { "file": "modules/inverter.vhgm" } } ], + "connections": [ { "from": "m.q", "to": "m.a" } ] } diff --git a/tools/vehicle_graph/tests/module-missing.vhg b/tools/vehicle_graph/tests/module-missing.vhg new file mode 100644 index 00000000..66eb1666 --- /dev/null +++ b/tools/vehicle_graph/tests/module-missing.vhg @@ -0,0 +1,3 @@ +// expect: error can't open file +{ "format": "maszyna-vehicle-graph", "version": 2, + "nodes": [ { "id": "m", "type": "module", "params": { "file": "modules/none.vhgm" } } ] } diff --git a/tools/vehicle_graph/tests/module-port.vhg b/tools/vehicle_graph/tests/module-port.vhg new file mode 100644 index 00000000..87cae8db --- /dev/null +++ b/tools/vehicle_graph/tests/module-port.vhg @@ -0,0 +1,4 @@ +// expect: error has no input "set" +{ "format": "maszyna-vehicle-graph", "version": 2, + "nodes": [ { "id": "c", "type": "logic.const" }, { "id": "m", "type": "module", "params": { "file": "modules/latch.vhgm" } } ], + "connections": [ { "from": "c.q", "to": "m.set" } ] } diff --git a/tools/vehicle_graph/tests/module-self.vhg b/tools/vehicle_graph/tests/module-self.vhg new file mode 100644 index 00000000..cf08401a --- /dev/null +++ b/tools/vehicle_graph/tests/module-self.vhg @@ -0,0 +1,3 @@ +// expect: error uses itself +{ "format": "maszyna-vehicle-graph", "version": 2, + "nodes": [ { "id": "m", "type": "module", "params": { "file": "modules/self.vhgm" } } ] } diff --git a/tools/vehicle_graph/tests/module.expected.csv b/tools/vehicle_graph/tests/module.expected.csv new file mode 100644 index 00000000..f8313cbf --- /dev/null +++ b/tools/vehicle_graph/tests/module.expected.csv @@ -0,0 +1,18 @@ +time;btn_on.pos;btn_on.index;btn_off.pos;btn_off.index;k1/sr.q;k1/inv/not.q;k1/inv2/not.q;k1/inv2/a.out;k2/sr.q;k2/inv/not.q;k2/inv2/not.q;k2/inv2/a.out;k2/note.out;c.q;sum.q;btn_on:position;btn_off:position;k1/sr:state;k2/sr:state;lamp:lit;lamp2:lit +0.1;0;0;0;0;0;1;1;0;1;0;1;0;2;7;9;0;0;0;1;0;1 +0.2;0;0;0;0;0;1;1;0;1;0;1;0;2;7;9;0;0;0;1;0;1 +0.3;1;1;0;0;1;0;1;0;0;1;1;0;2;7;9;1;0;1;0;1;1 +0.4;0;0;0;0;1;0;1;0;0;1;1;0;2;7;9;0;0;1;0;1;1 +0.5;0;0;0;0;1;0;1;0;0;1;1;0;2;7;9;0;0;1;0;1;1 +0.6;0;0;0;0;1;0;1;0;0;1;1;0;2;7;9;0;0;1;0;1;1 +0.7;0;0;1;1;0;1;1;0;1;0;1;0;2;7;9;0;1;0;1;0;1 +0.8;0;0;0;0;0;1;1;0;1;0;1;0;2;7;9;0;0;0;1;0;1 +0.9;0;0;0;0;0;1;1;0;1;0;1;0;2;7;9;0;0;0;1;0;1 +1;0;0;0;0;0;1;1;0;1;0;1;0;2;7;9;0;0;0;1;0;1 +1.1;0;0;0;0;0;1;1;0;1;0;1;0;2;7;9;0;0;0;1;0;1 +1.2;0;0;0;0;0;1;1;0;1;0;1;0;2;7;9;0;0;0;1;0;1 +1.3;0;0;0;0;0;1;1;0;1;0;1;0;2;7;9;0;0;0;1;0;1 +1.4;0;0;0;0;0;1;1;0;1;0;1;0;2;7;9;0;0;0;1;0;1 +1.5;0;0;0;0;0;1;1;0;1;0;1;0;2;7;9;0;0;0;1;0;1 +1.6;0;0;0;0;0;1;1;0;1;0;1;0;2;7;9;0;0;0;1;0;1 +1.7;0;0;0;0;0;1;1;0;1;0;1;0;2;7;9;0;0;0;1;0;1 diff --git a/tools/vehicle_graph/tests/module.script b/tools/vehicle_graph/tests/module.script new file mode 100644 index 00000000..0a6c224e --- /dev/null +++ b/tools/vehicle_graph/tests/module.script @@ -0,0 +1,4 @@ +0.2 press btn_on +0.3 release btn_on +0.6 press btn_off +0.7 release btn_off diff --git a/tools/vehicle_graph/tests/module.vhg b/tools/vehicle_graph/tests/module.vhg new file mode 100644 index 00000000..840f823e --- /dev/null +++ b/tools/vehicle_graph/tests/module.vhg @@ -0,0 +1,23 @@ +// expect: ok +// two instances of a module (with a nested module): ids "/", inputs, outputs, a passed input +{ + "format": "maszyna-vehicle-graph", "version": 2, + "nodes": [ + { "id": "btn_on", "type": "cab.switch", "params": { "behaviour": "monostable" } }, + { "id": "btn_off", "type": "cab.switch", "params": { "behaviour": "monostable" } }, + { "id": "k1", "type": "module", "params": { "file": "modules/latch.vhgm" } }, + { "id": "k2", "type": "module", "params": { "file": "modules\\latch.vhgm" } }, + { "id": "c", "type": "logic.const", "params": { "value": 7 } }, + { "id": "sum", "type": "logic.math", "params": { "op": "add" } }, + { "id": "lamp", "type": "fb.lamp" }, + { "id": "lamp2", "type": "fb.lamp" } + ], + "connections": [ + { "from": "btn_on.pos", "to": "k1.on" }, { "from": "btn_off.pos", "to": "k1.off" }, + { "from": "k1.q", "to": "lamp.on" }, + { "from": "k1.nq", "to": "k2.on" }, { "from": "k1.q", "to": "k2.off" }, + { "from": "c.q", "to": "k1.note" }, + { "from": "k1.echo", "to": "sum.a" }, { "from": "k2.echo", "to": "sum.b" }, + { "from": "k1.nq2", "to": "lamp2.on" } + ] +} diff --git a/tools/vehicle_graph/tests/modules/inverter.vhgm b/tools/vehicle_graph/tests/modules/inverter.vhgm new file mode 100644 index 00000000..44e828c3 --- /dev/null +++ b/tools/vehicle_graph/tests/modules/inverter.vhgm @@ -0,0 +1,10 @@ +// nested module of modules/latch.vhgm +{ + "format": "maszyna-vehicle-graph", "version": 2, + "nodes": [ + { "id": "a", "type": "module.input", "params": { "type": "bool" } }, + { "id": "not", "type": "logic.not" }, + { "id": "q", "type": "module.output", "params": { "type": "bool" } } + ], + "connections": [ { "from": "a.out", "to": "not.a" }, { "from": "not.q", "to": "q.in" } ] +} diff --git a/tools/vehicle_graph/tests/modules/latch.vhgm b/tools/vehicle_graph/tests/modules/latch.vhgm new file mode 100644 index 00000000..afc50ad8 --- /dev/null +++ b/tools/vehicle_graph/tests/modules/latch.vhgm @@ -0,0 +1,25 @@ +// module for tests/module.vhg: a latching relay with an inverted output (from a nested module), an input +// passed straight to an output, and a nested module with its input left unconnected (it sees the default value) +{ + "format": "maszyna-vehicle-graph", "version": 2, + "vehicle": { "name": "latch" }, + "nodes": [ + { "id": "on", "type": "module.input", "params": { "type": "bool", "label": "close" } }, + { "id": "off", "type": "module.input", "params": { "type": "bool", "label": "open" } }, + { "id": "note", "type": "module.input", "params": { "type": "float", "default": 2 } }, + { "id": "sr", "type": "logic.sr" }, + { "id": "inv", "type": "module", "params": { "file": "inverter.vhgm" } }, + { "id": "inv2", "type": "module", "params": { "file": "inverter.vhgm" } }, + { "id": "q", "type": "module.output", "params": { "type": "bool" } }, + { "id": "nq", "type": "module.output", "params": { "type": "bool" } }, + { "id": "echo", "type": "module.output", "params": { "type": "float" } }, + { "id": "nq2", "type": "module.output", "params": { "type": "bool" } } + ], + "connections": [ + { "from": "on.out", "to": "sr.s" }, { "from": "off.out", "to": "sr.r" }, + { "from": "sr.q", "to": "q.in" }, + { "from": "sr.q", "to": "inv.a" }, { "from": "inv.q", "to": "nq.in" }, + { "from": "note.out", "to": "echo.in" }, + { "from": "inv2.q", "to": "nq2.in" } + ] +} diff --git a/tools/vehicle_graph/tests/modules/self.vhgm b/tools/vehicle_graph/tests/modules/self.vhgm new file mode 100644 index 00000000..c5e36a87 --- /dev/null +++ b/tools/vehicle_graph/tests/modules/self.vhgm @@ -0,0 +1,2 @@ +{ "format": "maszyna-vehicle-graph", "version": 2, + "nodes": [ { "id": "again", "type": "module", "params": { "file": "self.vhgm" } } ] } diff --git a/tools/vehicle_graph/tests/multisystem.expected.csv b/tools/vehicle_graph/tests/multisystem.expected.csv new file mode 100644 index 00000000..bf767590 --- /dev/null +++ b/tools/vehicle_graph/tests/multisystem.expected.csv @@ -0,0 +1,47 @@ +time;up_dc.pos;up_dc.index;up_ac.pos;up_ac.index;ws_btn.pos;ws_btn.index;p_dc.up;p_dc.height;p_dc.u;p_ac.up;p_ac.height;p_ac.u;r_dc.ok;r_dc.u;r_ac.ok;r_ac.u;c_dc.q;c_ac.q;ws_dc.closed;ws_dc.i;ws_dc.tripped;ws_ac.closed;ws_ac.i;ws_ac.tripped;conv_on.q;conv.run;comp.run;res.p;gov.need;up_dc:position;up_ac:position;ws_btn:position;p_dc:height;p_ac:height;ws_dc:closed;ws_ac:closed;gov:need;lamp:lit;body:v;body:ft;body:fb;body:pg;body:zg;body:bc;body:mains +0.5;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;4.12435631;1;0;0;0;0;0;0;0;1;0;0;0;0;3.49790869;4.12435631;0;0 +1;1;1;0;0;0;0;0;0.25;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;4.12371227;1;1;0;0;0.25;0;0;0;1;0;0;0;0;3.49581862;4.12371227;0;0 +1.5;1;1;0;0;0;0;0;0.5;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;4.12306824;1;1;0;0;0.5;0;0;0;1;0;0;0;0;3.49372981;4.12306824;0;0 +2;1;1;0;0;0;0;0;0.75;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;4.1224242;1;1;0;0;0.75;0;0;0;1;0;0;0;0;3.49164224;4.1224242;0;0 +2.5;1;1;0;0;0;0;1;1;3000;0;0;0;0;3000;0;0;0;0;0;0;0;0;0;0;1;0;0;4.12178017;1;1;0;0;1;0;0;0;1;0;0;0;0;3.48955593;4.12178017;0;0 +3;1;1;0;0;0;0;1;1;3000;0;0;0;1;3000;0;0;0;0;0;0;0;0;0;0;1;0;0;4.12113615;1;1;0;0;1;0;0;0;1;0;0;0;0;3.48747085;4.12113615;0;0 +3.5;1;1;0;0;0;0;1;1;3000;0;0;0;1;3000;0;0;0;0;0;0;0;0;0;0;1;0;0;4.12049213;1;1;0;0;1;0;0;0;1;0;0;0;0;3.48538703;4.12049213;0;0 +4;1;1;0;0;0;0;1;1;3000;0;0;0;1;3000;0;0;0;0;0;0;0;0;0;0;1;0;0;4.11984811;1;1;0;0;1;0;0;0;1;0;0;0;0;3.48330445;4.11984811;0;0 +4.5;1;1;0;0;1;1;1;1;3000;0;0;0;1;3000;0;0;1;0;1;0;0;0;0;0;1;0;0;4.1192041;1;1;0;1;1;0;1;0;1;0;0;0;0;3.48122311;4.1192041;0;1 +5;1;1;0;0;0;0;1;1;3000;0;0;0;1;3000;0;0;0;0;1;0;0;0;0;0;1;0;0;4.11856009;1;1;0;0;1;0;1;0;1;0;0;0;0;3.47914302;4.11856009;0;1 +5.5;1;1;0;0;0;0;1;1;3000;0;0;0;1;3000;0;0;0;0;1;4.40666667;0;0;0;0;1;1;1;4.14835008;1;1;0;0;1;0;1;0;1;1;0;0;0;3.47706417;4.14835008;0;1 +6;1;1;0;0;0;0;1;1;3000;0;0;0;1;3000;0;0;0;0;1;4.40666667;0;0;0;0;1;1;1;4.1966562;1;1;0;0;1;0;1;0;1;1;0;0;0;3.47498656;4.1966562;0;1 +6.5;1;1;0;0;0;0;1;1;3000;0;0;0;1;3000;0;0;0;0;1;4.40666667;0;0;0;0;1;1;1;4.24481285;1;1;0;0;1;0;1;0;1;1;0;0;0;3.47291019;4.24481285;0;1 +7;1;1;0;0;0;0;1;1;3000;0;0;0;1;3000;0;0;0;0;1;4.40666667;0;0;0;0;1;1;1;4.2928205;1;1;0;0;1;0;1;0;1;1;0;0;0;3.47083507;4.2928205;0;1 +7.5;1;1;0;0;0;0;1;1;3000;0;0;0;1;3000;0;0;0;0;1;4.40666667;0;0;0;0;1;1;1;4.34067961;1;1;0;0;1;0;1;0;1;1;0;0;0;3.46876118;4.34067961;0;1 +8;1;1;0;0;0;0;1;1;3000;0;0;0;1;3000;0;0;0;0;1;4.40666667;0;0;0;0;1;1;1;4.38839064;1;1;0;0;1;0;1;0;1;1;0;0;0;3.46668853;4.38839064;0;1 +8.5;0;0;0;0;0;0;0;0.5;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;4.38870135;1;0;0;0;0.5;0;0;0;1;0;0;0;0;3.46461712;4.38870135;0;0 +9;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;4.38805568;1;0;0;0;0;0;0;0;1;0;0;0;0;3.46254695;4.38805568;0;0 +9.5;0;0;1;1;0;0;0;0;0;0;0.25;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;4.38741001;1;0;1;0;0;0.25;0;0;1;0;0;0;0;3.46047802;4.38741001;0;0 +10;0;0;1;1;0;0;0;0;0;0;0.5;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;4.38676434;1;0;1;0;0;0.5;0;0;1;0;0;0;0;3.45841032;4.38676434;0;0 +10.5;0;0;1;1;0;0;0;0;0;0;0.75;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;4.38611867;1;0;1;0;0;0.75;0;0;1;0;0;0;0;3.45634386;4.38611867;0;0 +11;0;0;1;1;0;0;0;0;0;1;1;25000;0;0;0;25000;0;0;0;0;0;0;0;0;1;0;0;4.38547301;1;0;1;0;0;1;0;0;1;0;0;0;0;3.45427863;4.38547301;0;0 +11.5;0;0;1;1;0;0;0;0;0;1;1;25000;0;0;1;25000;0;0;0;0;0;0;0;0;1;0;0;4.38482736;1;0;1;0;0;1;0;0;1;0;0;0;0;3.45221464;4.38482736;0;0 +12;0;0;1;1;0;0;0;0;0;1;1;25000;0;0;1;25000;0;0;0;0;0;0;0;0;1;0;0;4.38418171;1;0;1;0;0;1;0;0;1;0;0;0;0;3.45015188;4.38418171;0;0 +12.5;0;0;1;1;1;1;0;0;0;1;1;25000;0;0;1;25000;0;1;0;0;0;1;0;0;1;0;0;4.38353606;1;0;1;1;0;1;0;1;1;0;0;0;0;3.44809035;4.38353606;0;1 +13;0;0;1;1;0;0;0;0;0;1;1;25000;0;0;1;25000;0;0;0;0;0;1;0;0;1;0;0;4.38289041;1;0;1;0;0;1;0;1;1;0;0;0;0;3.44603006;4.38289041;0;1 +13.5;0;0;1;1;0;0;0;0;0;1;1;25000;0;0;1;25000;0;0;0;0;0;1;0.550833333;0;1;1;1;4.412172;1;0;1;0;0;1;0;1;1;1;0;0;0;3.44397099;4.412172;0;1 +14;0;0;1;1;0;0;0;0;0;1;1;25000;0;0;1;25000;0;0;0;0;0;1;0.550833333;0;1;1;1;4.45966182;1;0;1;0;0;1;0;1;1;1;0;0;0;3.44191316;4.45966182;0;1 +14.5;0;0;1;1;0;0;0;0;0;1;1;25000;0;0;1;25000;0;0;0;0;0;1;0.550833333;0;1;1;1;4.5070047;1;0;1;0;0;1;0;1;1;1;0;0;0;3.43985655;4.5070047;0;1 +15;0;0;1;1;0;0;0;0;0;1;1;25000;0;0;1;25000;0;0;0;0;0;1;0.550833333;0;1;1;1;4.5542011;1;0;1;0;0;1;0;1;1;1;0;0;0;3.43780118;4.5542011;0;1 +15.5;0;0;1;1;0;0;0;0;0;1;1;25000;0;0;1;25000;0;0;0;0;0;1;0.550833333;0;1;1;1;4.60125146;1;0;1;0;0;1;0;1;1;1;0;0;0;3.43574703;4.60125146;0;1 +16;0;0;1;1;0;0;0;0;0;1;1;25000;0;0;1;25000;0;0;0;0;0;1;0.550833333;0;1;1;1;4.64815625;1;0;1;0;0;1;0;1;1;1;0;0;0;3.43369411;4.64815625;0;1 +16.5;1;1;1;1;0;0;0;0.25;0;1;1;25000;0;0;1;25000;0;0;0;0;0;1;0.550833333;0;1;1;1;4.69491591;1;1;1;0;0.25;1;0;1;1;1;0;0;0;3.43164241;4.69491591;0;1 +17;1;1;1;1;0;0;0;0.5;0;1;1;25000;0;0;1;25000;0;0;0;0;0;1;0.550833333;0;1;1;1;4.74153088;1;1;1;0;0.5;1;0;1;1;1;0;0;0;3.42959195;4.74153088;0;1 +17.5;1;1;1;1;0;0;0;0.75;0;1;1;25000;0;0;1;25000;0;0;0;0;0;1;0.550833333;0;1;1;1;4.78800163;1;1;1;0;0.75;1;0;1;1;1;0;0;0;3.4275427;4.78800163;0;1 +18;1;1;1;1;0;0;1;1;3000;1;1;25000;0;3000;1;25000;0;0;0;0;0;1;0.550833333;0;1;1;1;4.83432858;1;1;1;0;1;1;0;1;1;1;0;0;0;3.42549469;4.83432858;0;1 +18.5;1;1;1;1;0;0;1;1;3000;1;1;25000;1;3000;1;25000;0;0;0;0;0;1;0.550833333;0;1;1;1;4.8805122;1;1;1;0;1;1;0;1;1;1;0;0;0;3.42344789;4.8805122;0;1 +19;1;1;1;1;0;0;1;1;3000;1;1;25000;1;3000;1;25000;0;0;0;0;0;1;0.550833333;0;1;1;1;4.92655292;1;1;1;0;1;1;0;1;1;1;0;0;0;3.42140232;4.92655292;0;1 +19.5;1;1;1;1;1;1;1;1;3000;1;1;25000;1;3000;1;25000;1;1;1;2.20333333;0;1;0.275416667;0;1;1;1;4.97245118;1;1;1;1;1;1;1;1;1;1;0;0;0;3.41935797;4.97245118;0;1 +20;1;1;1;1;0;0;1;1;3000;1;1;25000;1;3000;1;25000;0;0;1;2.20333333;0;1;0.275416667;0;1;1;1;5.01820742;1;1;1;0;1;1;1;1;1;1;0;0;0;3.41731484;5.01820742;0;1 +20.5;1;1;1;1;0;0;1;1;3000;1;1;25000;1;3000;1;25000;0;0;1;2.20333333;0;1;0.275416667;0;1;1;1;5.06382209;1;1;1;0;1;1;1;1;1;1;0;0;0;3.41527294;5.06382209;0;1 +21;1;1;1;1;0;0;1;1;3000;1;1;25000;1;3000;1;25000;0;0;1;2.20333333;0;1;0.275416667;0;1;1;1;5.10929563;1;1;1;0;1;1;1;1;1;1;0;0;0;3.41323225;5.10929563;0;1 +21.5;1;1;1;1;0;0;1;1;3000;1;1;25000;1;3000;1;25000;0;0;1;2.20333333;0;1;0.275416667;0;1;1;1;5.15462846;1;1;1;0;1;1;1;1;1;1;0;0;0;3.41119278;5.15462846;0;1 +22;1;1;1;1;0;0;1;1;3000;1;1;25000;1;3000;1;25000;0;0;1;2.20333333;0;1;0.275416667;0;1;1;1;5.19982103;1;1;1;0;1;1;1;1;1;1;0;0;0;3.40915453;5.19982103;0;1 +22.5;1;1;0;0;0;0;1;1;3000;0;0.5;0;1;3000;0;0;0;0;1;4.40666667;0;0;0;0;1;1;1;5.24487376;1;1;0;0;1;0.5;1;0;1;1;0;0;0;3.4071175;5.24487376;0;1 +23;1;1;0;0;0;0;1;1;3000;0;0;0;1;3000;0;0;0;0;1;4.40666667;0;0;0;0;1;1;1;5.2897871;1;1;0;0;1;0;1;0;1;1;0;0;0;3.40508169;5.2897871;0;1 diff --git a/tools/vehicle_graph/tests/multisystem.script b/tools/vehicle_graph/tests/multisystem.script new file mode 100644 index 00000000..2d30a871 --- /dev/null +++ b/tools/vehicle_graph/tests/multisystem.script @@ -0,0 +1,16 @@ +# events for: -graph-run multisystem.vhg -script multisystem.script +# options: -every 50 +# dc pantograph up, dc breaker closed, then the pantograph lowered: the relay drops and the breaker opens +0.5 toggle up_dc +4.0 press ws_btn +4.5 release ws_btn +8.0 toggle up_dc +# ac pantograph up, ac breaker closed: 25 kV through the transformer gives 3000 V on the bus +9.0 toggle up_ac +12.0 press ws_btn +12.5 release ws_btn +# both systems at once (preview only), then the ac pantograph lowered: mains stays closed with the dc breaker +16.0 toggle up_dc +19.0 press ws_btn +19.5 release ws_btn +22.0 toggle up_ac diff --git a/tools/vehicle_graph/tests/multisystem.vhg b/tools/vehicle_graph/tests/multisystem.vhg new file mode 100644 index 00000000..530acad9 --- /dev/null +++ b/tools/vehicle_graph/tests/multisystem.vhg @@ -0,0 +1,59 @@ +// expect: ok +// two-system vehicle (3 kV dc and 25 kV ac): voltage relays recognize the system on each pantograph, each system has +// its own line breaker, the ac branch has a transformer, both branches feed one bus with the converter (230 V~ auxiliary +// circuit for the compressor, 110 V= low voltage for a lamp). body:mains is closed when any of the breakers is +{ + "format": "maszyna-vehicle-graph", + "version": 2, + "vehicle": { "name": "multi-system test" }, + "settings": { "low_voltage_simulation": true }, + "body": { "mass": 80000, "length": 16, "wheel_diameter": 1.25, "axle_arrangement": "Bo'Bo'", "vmax": 120, "power": 4000 }, + "nodes": [ + { "id": "up_dc", "type": "cab.switch", "params": { "behaviour": "bistable" } }, + { "id": "up_ac", "type": "cab.switch", "params": { "behaviour": "bistable" } }, + { "id": "ws_btn", "type": "cab.switch", "params": { "behaviour": "monostable" } }, + { "id": "p_dc", "type": "el.pantograph", "params": { "index": 0, "raise_time": 2, "lower_time": 1, "min_pressure": 0, "preview_voltage": 3000 } }, + { "id": "p_ac", "type": "el.pantograph", "params": { "index": 1, "raise_time": 2, "lower_time": 1, "min_pressure": 0, "preview_voltage": 25000 } }, + { "id": "r_dc", "type": "el.voltage_relay", "params": { "min_voltage": 2200, "max_voltage": 4000, "delay": 0.5 } }, + { "id": "r_ac", "type": "el.voltage_relay", "params": { "min_voltage": 17500, "max_voltage": 30000, "delay": 0.5 } }, + { "id": "c_dc", "type": "logic.and" }, + { "id": "c_ac", "type": "logic.and" }, + { "id": "ws_dc", "type": "el.hsb", "params": { "trip_current": 0, "close_time": 0.2, "min_voltage": 2200, "init_time": 0 } }, + { "id": "ws_ac", "type": "el.hsb", "params": { "trip_current": 0, "close_time": 0.2, "min_voltage": 17500, "init_time": 0 } }, + { "id": "tr", "type": "el.transformer", "params": { "ratio": 0.12, "eff": 0.96 } }, + { "id": "bus", "type": "el.junction" }, + { "id": "conv_on", "type": "logic.const", "params": { "value": 1 } }, + { "id": "conv", "type": "el.converter", "params": { "power": 20000, "start_time": 1, "min_voltage": 2000, "out_voltage": 230, "nn_voltage": 110, "aux_kind": "ac1" } }, + { "id": "comp", "type": "pn.compressor", "params": { "power": 12000, "min_voltage": 200 } }, + { "id": "res", "type": "pn.reservoir", "params": { "volume": 0.8 } }, + { "id": "gov", "type": "pn.governor", "params": { "min": 7.5, "max": 8.5 } }, + { "id": "lamp", "type": "fb.lamp", "params": { "power": 220, "min_voltage": 70 } } + ], + "connections": [ + { "from": "up_dc.pos", "to": "p_dc.raise" }, + { "from": "up_ac.pos", "to": "p_ac.raise" }, + { "from": "p_dc.wn", "to": "r_dc.in" }, + { "from": "p_ac.wn", "to": "r_ac.in" }, + { "from": "p_dc.wn", "to": "ws_dc.in" }, + { "from": "p_ac.wn", "to": "ws_ac.in" }, + { "from": "ws_btn.pos", "to": "c_dc.a" }, + { "from": "r_dc.ok", "to": "c_dc.b" }, + { "from": "ws_btn.pos", "to": "c_ac.a" }, + { "from": "r_ac.ok", "to": "c_ac.b" }, + { "from": "c_dc.q", "to": "ws_dc.close" }, + { "from": "r_dc.ok", "to": "ws_dc.hold" }, + { "from": "c_ac.q", "to": "ws_ac.close" }, + { "from": "r_ac.ok", "to": "ws_ac.hold" }, + { "from": "ws_ac.out", "to": "tr.in" }, + { "from": "ws_dc.out", "to": "bus.a" }, + { "from": "tr.out", "to": "bus.b" }, + { "from": "bus.out", "to": "conv.wn" }, + { "from": "conv_on.q", "to": "conv.on" }, + { "from": "conv.aux", "to": "comp.power" }, + { "from": "gov.need", "to": "comp.on" }, + { "from": "comp.air", "to": "res.in" }, + { "from": "res.p", "to": "gov.p" }, + { "from": "conv.nn", "to": "lamp.supply" }, + { "from": "conv.run", "to": "lamp.on" } + ] +} diff --git a/tools/vehicle_graph/tests/profile-version.vhg b/tools/vehicle_graph/tests/profile-version.vhg new file mode 100644 index 00000000..c135b19c --- /dev/null +++ b/tools/vehicle_graph/tests/profile-version.vhg @@ -0,0 +1,2 @@ +// expect: error interface profile "brake_pipe" version 2 is newer than the supported version 1 +{ "format": "maszyna-vehicle-graph", "version": 2, "profiles": { "brake_pipe": 2 }, "nodes": [ { "id": "c", "type": "logic.const" } ] } diff --git a/tools/vehicle_graph/tests/security.expected.csv b/tools/vehicle_graph/tests/security.expected.csv new file mode 100644 index 00000000..9d288f54 --- /dev/null +++ b/tools/vehicle_graph/tests/security.expected.csv @@ -0,0 +1,64 @@ +time;reset.pos;reset.index;speed.q;power.q;ca.lamp;ca.buzzer;ca.brake;ca.shp_lamp;res.p;gov.need;brake.q;fv4a.p_ctl;pipe.p;reset:position;gov:need;body:v;body:ft;body:fb;body:pg;body:zg;body:bc +0.5;0;0;50;1;0;0;0;0;7.34933658;1;0;0;4.98972303;0;1;0;0;0;4.98972303;7.34933658;0 +1;0;0;50;1;0;0;0;0;7.34867259;1;0;0;4.98644354;0;1;0;0;0;4.98644354;7.34867259;0 +1.5;0;0;50;1;0;0;0;0;7.3480086;1;0;0;4.9831662;0;1;0;0;0;4.9831662;7.3480086;0 +2;0;0;50;1;0;0;0;0;7.34734462;1;0;0;4.97989102;0;1;0;0;0;4.97989102;7.34734462;0 +2.5;0;0;50;1;0;0;0;0;7.34668065;1;0;0;4.97661798;0;1;0;0;0;4.97661798;7.34668065;0 +3;0;0;50;1;0;0;0;0;7.34601668;1;0;0;4.9733471;0;1;0;0;0;4.9733471;7.34601668;0 +3.5;0;0;50;1;0;0;0;0;7.34535271;1;0;0;4.97007837;0;1;0;0;0;4.97007837;7.34535271;0 +4;0;0;50;1;0;0;0;0;7.34468874;1;0;0;4.96681179;0;1;0;0;0;4.96681179;7.34468874;0 +4.5;0;0;50;1;0;0;0;0;7.34402478;1;0;0;4.96354735;0;1;0;0;0;4.96354735;7.34402478;0 +5;0;0;50;1;0;0;0;0;7.34336083;1;0;0;4.96028506;0;1;0;0;0;4.96028506;7.34336083;0 +5.5;0;0;50;1;0;0;0;0;7.34269688;1;0;0;4.95702492;0;1;0;0;0;4.95702492;7.34269688;0 +6;0;0;50;1;0;0;0;0;7.34203293;1;0;0;4.95376691;0;1;0;0;0;4.95376691;7.34203293;0 +6.5;0;0;50;1;0;0;0;0;7.34136899;1;0;0;4.95051105;0;1;0;0;0;4.95051105;7.34136899;0 +7;0;0;50;1;0;0;0;0;7.34070505;1;0;0;4.94725733;0;1;0;0;0;4.94725733;7.34070505;0 +7.5;0;0;50;1;0;0;0;0;7.34004111;1;0;0;4.94400575;0;1;0;0;0;4.94400575;7.34004111;0 +8;0;0;50;1;0;0;0;0;7.33937718;1;0;0;4.9407563;0;1;0;0;0;4.9407563;7.33937718;0 +8.5;0;0;50;1;0;0;0;0;7.33871326;1;0;0;4.93750899;0;1;0;0;0;4.93750899;7.33871326;0 +9;0;0;50;1;0;0;0;0;7.33804933;1;0;0;4.93426381;0;1;0;0;0;4.93426381;7.33804933;0 +9.5;0;0;50;1;0;0;0;0;7.33738542;1;0;0;4.93102077;0;1;0;0;0;4.93102077;7.33738542;0 +10;0;0;50;1;0;0;0;0;7.3367215;1;0;0;4.92777986;0;1;0;0;0;4.92777986;7.3367215;0 +10.5;0;0;50;1;0;0;0;0;7.33605759;1;0;0;4.92454108;0;1;0;0;0;4.92454108;7.33605759;0 +11;0;0;50;1;0;0;0;0;7.33539369;1;0;0;4.92130442;0;1;0;0;0;4.92130442;7.33539369;0 +11.5;0;0;50;1;0;0;0;0;7.33472978;1;0;0;4.9180699;0;1;0;0;0;4.9180699;7.33472978;0 +12;0;0;50;1;0;0;0;0;7.33406589;1;0;0;4.9148375;0;1;0;0;0;4.9148375;7.33406589;0 +12.5;0;0;50;1;0;0;0;0;7.33340199;1;0;0;4.91160722;0;1;0;0;0;4.91160722;7.33340199;0 +13;0;0;50;1;0;0;0;0;7.3327381;1;0;0;4.90837907;0;1;0;0;0;4.90837907;7.3327381;0 +13.5;0;0;50;1;0;0;0;0;7.33207422;1;0;0;4.90515304;0;1;0;0;0;4.90515304;7.33207422;0 +14;0;0;50;1;0;0;0;0;7.33141033;1;0;0;4.90192913;0;1;0;0;0;4.90192913;7.33141033;0 +14.5;0;0;50;1;0;0;0;0;7.33074646;1;0;0;4.89870734;0;1;0;0;0;4.89870734;7.33074646;0 +15;0;0;50;1;0;0;0;0;7.33008258;1;0;0;4.89548766;0;1;0;0;0;4.89548766;7.33008258;0 +15.5;0;0;50;1;0;0;0;0;7.32941872;1;0;0;4.8922701;0;1;0;0;0;4.8922701;7.32941872;0 +16;0;0;50;1;0;0;0;0;7.32875485;1;0;0;4.88905466;0;1;0;0;0;4.88905466;7.32875485;0 +16.5;0;0;50;1;0;0;0;0;7.32809099;1;0;0;4.88584133;0;1;0;0;0;4.88584133;7.32809099;0 +17;0;0;50;1;0;0;0;0;7.32742713;1;0;0;4.88263011;0;1;0;0;0;4.88263011;7.32742713;0 +17.5;0;0;50;1;0;0;0;0;7.32676328;1;0;0;4.87942101;0;1;0;0;0;4.87942101;7.32676328;0 +18;0;0;50;1;0;0;0;0;7.32609943;1;0;0;4.87621401;0;1;0;0;0;4.87621401;7.32609943;0 +18.5;0;0;50;1;1;0;0;0;7.32543559;1;0;0;4.87300912;0;1;0;0;0;4.87300912;7.32543559;0 +19;0;0;50;1;1;0;0;0;7.32477175;1;0;0;4.86980633;0;1;0;0;0;4.86980633;7.32477175;0 +19.5;0;0;50;1;1;0;0;0;7.32410791;1;0;0;4.86660565;0;1;0;0;0;4.86660565;7.32410791;0 +20;0;0;50;1;1;1;0;0;7.32344408;1;0;0;4.86340708;0;1;0;0;0;4.86340708;7.32344408;0 +20.5;0;0;50;1;1;1;0;0;7.32278025;1;0;0;4.86021061;0;1;0;0;0;4.86021061;7.32278025;0 +21;0;0;50;1;1;1;0;0;7.32211643;1;0;0;4.85701623;0;1;0;0;0;4.85701623;7.32211643;0 +21.5;0;0;50;1;1;1;0;0;7.32145261;1;0;0;4.85382396;0;1;0;0;0;4.85382396;7.32145261;0 +22;0;0;50;1;1;1;0;0;7.32078879;1;0;0;4.85063379;0;1;0;0;0;4.85063379;7.32078879;0 +22.5;0;0;50;1;1;1;0;0;7.32012498;1;0;0;4.84744571;0;1;0;0;0;4.84744571;7.32012498;0 +23;0;0;50;1;1;1;0;0;7.31946117;1;0;0;4.84425973;0;1;0;0;0;4.84425973;7.31946117;0 +23.5;0;0;50;1;1;1;1;0;7.31879737;1;0;0;0.0986824494;0;1;0;0;0;0.0986824494;7.31879737;0 +24;0;0;50;1;1;1;1;0;7.31813357;1;0;0;0.000134562421;0;1;0;0;0;0.000134562421;7.31813357;0 +24.5;0;0;50;1;1;1;1;0;7.31746977;1;0;0;1.97039664e-07;0;1;0;0;0;1.97039664e-07;7.31746977;0 +25;0;0;50;1;1;1;1;0;7.31680598;1;0;0;2.88554442e-10;0;1;0;0;0;2.88554442e-10;7.31680598;0 +25.5;0;0;50;1;1;1;1;0;7.31614219;1;0;0;4.22557222e-13;0;1;0;0;0;4.22557222e-13;7.31614219;0 +26;0;0;50;1;1;1;1;0;7.31547841;1;0;0;6.23571712e-16;0;1;0;0;0;6.23571712e-16;7.31547841;0 +26.5;0;0;50;1;1;1;1;0;7.31481463;1;0;0;1.07323749e-16;0;1;0;0;0;1.07323749e-16;7.31481463;0 +27;0;0;50;1;1;1;1;0;7.31415086;1;0;0;1.07317334e-16;0;1;0;0;0;1.07317334e-16;7.31415086;0 +27.5;0;0;50;1;1;1;1;0;7.31348709;1;0;0;1.0731092e-16;0;1;0;0;0;1.0731092e-16;7.31348709;0 +28;0;0;50;1;1;1;1;0;7.31282332;1;0;0;1.07304507e-16;0;1;0;0;0;1.07304507e-16;7.31282332;0 +28.5;0;0;50;1;1;1;1;0;7.31215956;1;0;0;1.07298093e-16;0;1;0;0;0;1.07298093e-16;7.31215956;0 +29;0;0;50;1;1;1;1;0;7.3114958;1;0;0;1.0729168e-16;0;1;0;0;0;1.0729168e-16;7.3114958;0 +29.5;0;0;50;1;1;1;1;0;7.31083205;1;0;0;1.07285268e-16;0;1;0;0;0;1.07285268e-16;7.31083205;0 +30;0;0;50;1;1;1;1;0;7.3101683;1;0;0;1.07278856e-16;0;1;0;0;0;1.07278856e-16;7.3101683;0 +30.5;0;0;50;1;0;0;0;0;7.30950455;1;0;0;1.07272444e-16;0;1;0;0;0;1.07272444e-16;7.30950455;0 +31;0;0;50;1;0;0;0;0;7.30884081;1;0;0;1.07266032e-16;0;1;0;0;0;1.07266032e-16;7.30884081;0 +31.2;0;0;50;1;0;0;0;0;7.30857531;1;0;0;1.07263468e-16;0;1;0;0;0;1.07263468e-16;7.30857531;0 diff --git a/tools/vehicle_graph/tests/security.script b/tools/vehicle_graph/tests/security.script new file mode 100644 index 00000000..ceb8560e --- /dev/null +++ b/tools/vehicle_graph/tests/security.script @@ -0,0 +1,6 @@ +# events for: -graph-run security.vhg -script security.script +# options: -state ready -every 50 +8.0 press reset +8.2 release reset +30.0 press reset +30.2 release reset diff --git a/tools/vehicle_graph/tests/security.vhg b/tools/vehicle_graph/tests/security.vhg new file mode 100644 index 00000000..67bd06d0 --- /dev/null +++ b/tools/vehicle_graph/tests/security.vhg @@ -0,0 +1,30 @@ +// expect: ok +// vigilance device at 50 km/h without acknowledgement: lamp after the aware delay, buzzer after the sound delay, +// then emergency braking through the brake pipe; acknowledged in time once, then left alone +{ + "format": "maszyna-vehicle-graph", + "version": 2, + "vehicle": { "name": "security test" }, + "body": { "mass": 80000, "length": 16, "wheel_diameter": 1.25, "axle_arrangement": "Bo'Bo'", "vmax": 120 }, + "nodes": [ + { "id": "reset", "type": "cab.switch", "params": { "behaviour": "monostable" } }, + { "id": "speed", "type": "logic.const", "params": { "value": 50 } }, + { "id": "power", "type": "logic.const", "params": { "value": 1 } }, + { "id": "ca", "type": "sec.ca", "params": { "aware": true, "cab_signal": false, "aware_delay": 10, "sound_delay": 2, "emergency_delay": 3, "min_speed": 10 } }, + { "id": "res", "type": "pn.reservoir", "params": { "volume": 0.8 } }, + { "id": "gov", "type": "pn.governor", "params": { "min": 7.5, "max": 8.5 } }, + { "id": "brake", "type": "logic.const", "params": { "value": 0 } }, + { "id": "fv4a", "type": "br.handle", "params": { "type": "FV4a" } }, + { "id": "pipe", "type": "br.pipe" } + ], + "connections": [ + { "from": "speed.q", "to": "ca.v" }, + { "from": "power.q", "to": "ca.power" }, + { "from": "reset.pos", "to": "ca.reset" }, + { "from": "ca.brake", "to": "pipe.vent" }, + { "from": "res.out", "to": "fv4a.zg" }, + { "from": "brake.q", "to": "fv4a.pos" }, + { "from": "fv4a.pg", "to": "pipe.in" }, + { "from": "res.p", "to": "gov.p" } + ] +} diff --git a/tools/vehicle_graph/tests/single.vhg b/tools/vehicle_graph/tests/single.vhg new file mode 100644 index 00000000..6a95e4a9 --- /dev/null +++ b/tools/vehicle_graph/tests/single.vhg @@ -0,0 +1,2 @@ +// expect: error the graph can have only one block of type "br.pipe" +{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "a", "type": "br.pipe" }, { "id": "b", "type": "br.pipe" } ] } diff --git a/tools/vehicle_graph/tests/table-width.vhg b/tools/vehicle_graph/tests/table-width.vhg new file mode 100644 index 00000000..0ba0287e --- /dev/null +++ b/tools/vehicle_graph/tests/table-width.vhg @@ -0,0 +1,2 @@ +// expect: error row 2 has 3 values, expected 5 +{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "m", "type": "drive.dc_series", "params": { "rlist": [[0, 0, 0, 0, 0], [1, 2.5, 1]] } } ] } diff --git a/utilities/Globals.cpp b/utilities/Globals.cpp index b4f2e14b..2e83a756 100644 --- a/utilities/Globals.cpp +++ b/utilities/Globals.cpp @@ -911,6 +911,19 @@ bool global_settings::ConfigParseNetwork(cParser& Parser, const std::string& tok return true; } + if (token == "vehiclegraph_preview") + { + // live preview server of the vehicle graph editor + ParseOne(Parser, vehiclegraph_preview); + return true; + } + + if (token == "vehiclegraph_preview_port") + { + ParseOne(Parser, vehiclegraph_preview_port); + return true; + } + return false; } diff --git a/utilities/Globals.h b/utilities/Globals.h index 8f5748c3..f1047de8 100644 --- a/utilities/Globals.h +++ b/utilities/Globals.h @@ -356,6 +356,9 @@ struct global_settings { std::vector> network_servers; std::optional> network_client; + // live preview server of the vehicle graph editor (vehicle/graph/graph_preview.h) + bool vehiclegraph_preview{false}; + int vehiclegraph_preview_port{7420}; float desync = 0.0f; std::unordered_map trainset_overrides; diff --git a/vehicle/Button.cpp b/vehicle/Button.cpp index d4d31877..166a6e88 100644 --- a/vehicle/Button.cpp +++ b/vehicle/Button.cpp @@ -150,6 +150,21 @@ void TButton::AssignBool(bool const *bValue) { bData = bValue; } +void TButton::AssignSounds( std::string const &Increase, std::string const &Decrease, TDynamicObject const *Owner ) { + + if( false == Increase.empty() ) { + m_soundfxincrease.deserialize( Increase, sound_type::single ); + } + if( false == Decrease.empty() ) { + m_soundfxdecrease.deserialize( Decrease, sound_type::single ); + } + m_soundfxincrease.owner( Owner ); + m_soundfxdecrease.owner( Owner ); + auto const offset { model_offset() }; + m_soundfxincrease.offset( offset ); + m_soundfxdecrease.offset( offset ); +} + void TButton::play() { if( m_state == true ) { m_soundfxincrease.play(); } diff --git a/vehicle/Button.h b/vehicle/Button.h index e2eb38cb..4b6d49b6 100644 --- a/vehicle/Button.h +++ b/vehicle/Button.h @@ -33,6 +33,9 @@ public: bool Init( std::string const &asName, TModel3d const *pModel, bool bNewOn = false ); void Load( cParser &Parser, TDynamicObject const *Owner ); void AssignBool(bool const *bValue); + // sounds of a lamp defined outside the .mmd file (vehicle graph), in the format of soundinc: and sounddec:, + // played when the lamp lights and goes out. assign them after Init(), to place them at the submodel + void AssignSounds( std::string const &Increase, std::string const &Decrease, TDynamicObject const *Owner ); // returns offset of submodel associated with the button from the model centre glm::vec3 model_offset() const; void gain(float new_volume); diff --git a/vehicle/Driver.cpp b/vehicle/Driver.cpp index 75bbb29e..f37973ef 100644 --- a/vehicle/Driver.cpp +++ b/vehicle/Driver.cpp @@ -4833,6 +4833,14 @@ TController::Update( double const Timedelta ) { // uruchamiać przynajmniej raz na sekundę if( ( iDrivigFlags & movePrimary ) == 0 ) { return; } // pasywny nic nie robi if( false == simulation::is_ready ) { return; } + if( AIControllFlag && mvOccupied->Graph ) { + // the AI can't drive a vehicle defined by a graph (.vhg) yet; the vehicle is left as its graph keeps it + if( false == m_graphvehiclewarning ) { + WriteLog( "Warning: AI driver can't operate vehicle graph of \"" + mvOccupied->Name + "\"; the vehicle is left to the player" ); + m_graphvehiclewarning = true; + } + return; + } update_timers( Timedelta ); update_logs( Timedelta ); diff --git a/vehicle/Driver.h b/vehicle/Driver.h index da14b1b3..abb0ce48 100644 --- a/vehicle/Driver.h +++ b/vehicle/Driver.h @@ -371,6 +371,7 @@ private: // members public: bool AIControllFlag = false; // rzeczywisty/wirtualny maszynista + bool m_graphvehiclewarning { false }; // the AI was told it can't drive the graph vehicle int iOverheadZero = 0; // suma bitowa jezdy bezprądowej, bity ustawiane przez pojazdy z podniesionymi pantografami int iOverheadDown = 0; // suma bitowa opuszczenia pantografów, bity ustawiane przez pojazdy z podniesionymi pantografami double BrakeCtrlPosition = 0.0; // intermediate position of main brake controller diff --git a/vehicle/DynObj.cpp b/vehicle/DynObj.cpp index 32ba307f..0cc53fed 100644 --- a/vehicle/DynObj.cpp +++ b/vehicle/DynObj.cpp @@ -28,6 +28,9 @@ http://mozilla.org/MPL/2.0/. #include "world/Traction.h" #include "audio/sound.h" #include "model/MdlMngr.h" +#include "vehicle/graph/graph_instance.h" +#include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/blocks/cab.h" #include "model/Model3d.h" #include "rendering/renderer.h" #include "application/uitranscripts.h" @@ -831,6 +834,10 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) if (Global.UpdateMaterials) on_season_update(); + if( false == m_graphanimations.empty() ) { + update_graph_animations(); + } + if (ObjSqrDist < 400 * 400 ) // gdy bliżej niż 400m { for( auto &animation : pAnimations ) { @@ -1984,7 +1991,15 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" // utworzenie parametrów fizyki MoverParameters = new TMoverParameters(iDirection ? fVel : -fVel, Type_Name, asName, Cab); // McZapkie: TypeName musi byc nazwą CHK/MMD pojazdu - if (!MoverParameters->LoadFIZ(asBaseDir)) + // a vehicle graph (.vhg) next to the .mmd defines the vehicle in place of the .fiz + auto const graphfile { asBaseDir + Type_Name + std::string{ vgraph::file_extension } }; + if( FileExists( graphfile ) ) { + if( false == MoverParameters->LoadGraph( graphfile ) ) { + ErrorLog( "Bad vehicle: failed to load vehicle graph from file \"" + BaseDir + "/" + Type_Name + std::string{ vgraph::file_extension } + "\"" ); + return 0.0; + } + } + else if (!MoverParameters->LoadFIZ(asBaseDir)) { // jak wczytanie CHK się nie uda, to błąd if (ConversionError == 666) ErrorLog( "Bad vehicle: failed to locate definition file \"" + BaseDir + "/" + Type_Name + ".fiz" + "\"" ); @@ -2314,6 +2329,10 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" // wczytywanie z pliku nazwatypu.mmd, w tym model erase_extension( asReplacableSkin ); LoadMMediaFile(Type_Name, asReplacableSkin); + if( MoverParameters->Graph ) { + create_graph_sounds(); + create_graph_animations(); + } // McZapkie-100402: wyszukiwanie submodeli sprzegów btCoupler1.Init( "coupler1", mdModel ); // false - ma być wyłączony btCoupler2.Init( "coupler2", mdModel ); @@ -3193,6 +3212,10 @@ bool TDynamicObject::Update(double dt, double dt1) tmpTraction.TractionResistivity = 0.3; MoverParameters->PantographVoltage = tmpTraction.TractionVoltage; + if( MoverParameters->Graph ) { + // the headlights of a graph vehicle are dimmed by its graph (veh.lights) + DimHeadlights = MoverParameters->Graph->body().headlights_dimmed; + } // McZapkie: predkosc w torze przekazac do TrackParam // McZapkie: Vel ma wymiar [km/h] (absolutny), V ma wymiar [m/s], taka przyjalem notacje tp.Velmax = MyTrack->VelocityGet(); @@ -4353,14 +4376,171 @@ void TDynamicObject::TurnOff() btMechanik2.Turn( false ); btShutters1.Turn( false ); btShutters2.Turn( false ); + // elements of a graph vehicle back in their default (off) state, which the other vehicles sharing the model see + for( auto const &item : m_graphanimations ) { + if( item.kind != graph_animation::kind_type::visible ) { continue; } + if( item.submodel != nullptr ) { item.submodel->iVisible = 0; } + if( item.submodel_off != nullptr ) { item.submodel_off->iVisible = 1; } + } }; // przeliczanie dźwięków, bo będzie słychać bez wyświetlania sektora z pojazdem +void TDynamicObject::create_graph_sounds() { + + m_graphsounds.clear(); + auto &graph { *MoverParameters->Graph }; + auto const &nodes { graph.definition().nodes() }; + for( std::size_t idx = 0; idx < nodes.size(); ++idx ) { + auto const *block { dynamic_cast( &graph.node_block( idx ) ) }; + if( block == nullptr || block->file().empty() ) { continue; } + // the same placements as the sounds of the .mmd files + auto const placement { + block->placement() == vgraph::vehicle_sound::placement_type::internal ? sound_placement::internal : + block->placement() == vgraph::vehicle_sound::placement_type::engine ? sound_placement::engine : + block->placement() == vgraph::vehicle_sound::placement_type::external ? sound_placement::external : + block->placement() == vgraph::vehicle_sound::placement_type::external_ambient ? sound_placement::external_ambient : + sound_placement::general }; + sound_source sound { placement, static_cast( block->range() > 0.0 ? block->range() : EU07_SOUND_RUNNINGNOISECUTOFFRANGE ) }; + sound.deserialize( block->file(), sound_type::single ); + sound.owner( this ); + sound.offset( { static_cast( block->offset_x() ), static_cast( block->offset_y() ), static_cast( block->offset_z() ) } ); + m_graphsounds.push_back( { block, sound } ); + } +} + +void TDynamicObject::render_graph_sounds() { + + for( auto &item : m_graphsounds ) { + auto const &block { *item.block }; + auto &sound { item.sound }; + auto const activated { block.playing() && false == item.active }; + item.active = block.playing(); + if( false == item.active ) { + if( block.loop() && sound.is_playing() ) { + sound.stop(); + } + continue; + } + sound.gain( block.volume() ); + sound.pitch( block.pitch() ); + if( block.loop() ) { + sound.play( sound_flags::exclusive | sound_flags::looping ); + } + else if( activated ) { + // other sounds play once per activation of the input + sound.play( sound_flags::exclusive ); + } + } +} + +// the models are shared by all vehicles of the same type (TModelsManager), so the submodels hold the state of whichever +// vehicle set them last. the renderers call ABuLittleUpdate() for every vehicle right before drawing its models, with +// TSubModel::iInstance set to that vehicle, and TurnOff() after drawing them when btnOn is set: +// - SetRotate() and SetTranslate() stamp the submodel with iInstance (iAnimOwner), and the renderer applies the transform +// only while drawing the same instance, so a vehicle which doesn't set it draws the submodel at rest, as for the doors; +// - visibility has no owner, so like the TButtons of the lights the elements are shown in ABuLittleUpdate() only for +// the vehicle being drawn, and TurnOff() puts them back into the off state, which is also the state they're left in here +void TDynamicObject::create_graph_animations() { + + m_graphanimations.clear(); + auto &graph { *MoverParameters->Graph }; + auto const &nodes { graph.definition().nodes() }; + for( std::size_t idx = 0; idx < nodes.size(); ++idx ) { + auto const &node { nodes[ idx ] }; + auto const isanimation { node.type->id == "veh.anim" }; + if( false == isanimation && node.type->id != "veh.visible" ) { continue; } + // parameters by their keys, so this doesn't depend on the block classes + auto const param = [&]( std::string_view const Key ) -> std::string const & { + static std::string const none; + for( std::size_t paramidx = 0; paramidx < node.param_defs.size(); ++paramidx ) { + if( node.param_defs[ paramidx ].key == Key ) { + return node.params[ paramidx ].text; + } + } + return none; + }; + auto const &submodelname { param( "submodel" ) }; + auto const &submodeloffname { isanimation ? std::string{} : param( "submodel_off" ) }; + if( submodelname.empty() && submodeloffname.empty() ) { continue; } + auto const lowpoly { param( "model" ) == "lowpoly" }; + auto const *model { lowpoly ? mdLowPolyInt : mdModel }; + if( model == nullptr ) { + ErrorLog( "Bad model: block \"" + node.id + "\" of the vehicle graph uses the " + ( lowpoly ? "low poly interior" : "vehicle" ) + " model, which \"" + name() + "\" doesn't have", logtype::model ); + continue; + } + auto const find = [&]( std::string const &Name ) -> TSubModel * { + if( Name.empty() ) { return nullptr; } + auto *submodel { model->GetFromName( Name ) }; + if( submodel == nullptr ) { + ErrorLog( "Bad model: failed to locate sub-model \"" + Name + "\" of block \"" + node.id + "\" in the " + ( lowpoly ? "low poly interior" : "vehicle" ) + " model of \"" + name() + "\"", logtype::model ); + } + return submodel; + }; + graph_animation item; + item.block = &graph.node_block( idx ); + item.kind = ( + false == isanimation ? graph_animation::kind_type::visible : + param( "animation" ) == "move" ? graph_animation::kind_type::move : + graph_animation::kind_type::rotate ); + item.state = 0; + auto const statename { isanimation ? "anim" : "on" }; + for( std::size_t stateidx = 0; stateidx < item.block->state_count(); ++stateidx ) { + if( std::string_view{ item.block->state_name( stateidx ) } == statename ) { + item.state = stateidx; + } + } + item.submodel = find( submodelname ); + item.submodel_off = find( submodeloffname ); + if( item.submodel == nullptr && item.submodel_off == nullptr ) { continue; } + if( item.kind == graph_animation::kind_type::visible ) { + // the off state, like TButton::Init() + if( item.submodel != nullptr ) { item.submodel->iVisible = 0; } + if( item.submodel_off != nullptr ) { item.submodel_off->iVisible = 1; } + } + m_graphanimations.push_back( item ); + } +} + +void TDynamicObject::update_graph_animations() { + + for( auto const &item : m_graphanimations ) { + auto const value { item.block->state_value( item.state ) }; + switch( item.kind ) { + case graph_animation::kind_type::rotate: { + // degrees about the y axis, as TGauge (gt_Rotate) + item.submodel->SetRotate( float3( 0, 1, 0 ), static_cast( value ) ); + break; + } + case graph_animation::kind_type::move: { + // millimetres along the z axis, as TGauge (gt_Move) + item.submodel->SetTranslate( float3( 0, 0, static_cast( value * 0.001 ) ) ); + break; + } + default: { + auto const on { value > 0.5 }; + if( item.submodel != nullptr ) { item.submodel->iVisible = on; } + if( item.submodel_off != nullptr ) { item.submodel_off->iVisible = !on; } + if( on ) { + // restored by TurnOff() after drawing + btnOn = true; + } + break; + } + } + } +} + void TDynamicObject::RenderSounds() { if( false == simulation::is_ready ) { return; } if( Global.iPause != 0 ) { return; } + if( MoverParameters->Graph ) { + // a graph vehicle has only the sounds of its graph + render_graph_sounds(); + return; + } + if( m_startjoltplayed && ( std::abs(MoverParameters->AccSVBased) < 0.01 || GetVelocity() < 0.01 ) ) { @@ -5919,6 +6099,14 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co } // models + else if( token == "sounds:" && MoverParameters->Graph ) { + // sounds of a graph vehicle are defined by its graph + do { + token = ""; + parser.getTokens(); parser >> token; + } while( token != "" + && token != "endsounds" ); + } else if( token == "sounds:" ) { // dzwieki do { diff --git a/vehicle/DynObj.h b/vehicle/DynObj.h index 36335a32..6bbd068f 100644 --- a/vehicle/DynObj.h +++ b/vehicle/DynObj.h @@ -22,6 +22,12 @@ http://mozilla.org/MPL/2.0/. #include "audio/sound.h" #include "world/Spring.h" +namespace vgraph +{ +class block; +class vehicle_sound; +} // namespace vgraph + #include #define EU07_SOUND_BOGIESOUNDS @@ -565,6 +571,23 @@ private: bool m_startjoltplayed { false }; std::array m_couplersounds; // always front and rear std::vector m_pantographsounds; // typically 2 but can be less (or more?) + // sounds of a vehicle defined by a graph (fb.sound blocks), in place of the sounds of the .mmd + struct graph_sound { + vgraph::vehicle_sound const *block; + sound_source sound; + bool active { false }; + }; + std::vector m_graphsounds; + // submodels of the external model driven by the veh.anim and veh.visible blocks of a graph vehicle + struct graph_animation { + enum class kind_type { rotate, move, visible }; + vgraph::block const *block; + std::size_t state; // index of the block's state applied to the submodels + kind_type kind; + TSubModel *submodel; // animated, or shown when the state is on + TSubModel *submodel_off; // shown when the state is off; visibility only, optional + }; + std::vector m_graphanimations; std::vector m_doorsounds; // can expect symmetrical arrangement, but don't count on it bool m_doorlocks { false }; // sound helper, current state of door locks sound_source sHorn1 { sound_placement::external, 5 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; @@ -703,6 +726,13 @@ private: void Move(double fDistance); void FastMove(double fDistance); void RenderSounds(); + // creates the sounds of the fb.sound blocks of a graph vehicle + void create_graph_sounds(); + void render_graph_sounds(); + // finds the submodels of the veh.anim and veh.visible blocks of a graph vehicle + void create_graph_animations(); + // applies the state of these blocks to the shared model, right before the vehicle is drawn + void update_graph_animations(); inline glm::dvec3 GetPosition() const { return vPosition; }; // converts location from vehicle coordinates frame to world frame diff --git a/vehicle/Gauge.cpp b/vehicle/Gauge.cpp index 63c042d3..d34af230 100644 --- a/vehicle/Gauge.cpp +++ b/vehicle/Gauge.cpp @@ -446,6 +446,29 @@ void TGauge::AssignState( bool const *State ) { m_stateinput = State; } +void TGauge::AssignSounds( std::string const &Increase, std::string const &Decrease, std::string const &Positions, TDynamicObject const *Owner ) { + + if( false == Increase.empty() ) { + m_soundfxincrease.deserialize( Increase, sound_type::single ); + } + if( false == Decrease.empty() ) { + m_soundfxdecrease.deserialize( Decrease, sound_type::single ); + } + if( false == Positions.empty() ) { + // the same entries as in the block of a control in the .mmd file + cParser parser { Positions }; + scratch_data scratchpad; + while( true == Load_mapping( parser, scratchpad ) ) { + ; // all work done by while() + } + } + m_soundfxincrease.owner( Owner ); + m_soundfxdecrease.owner( Owner ); + for( auto &soundfxrecord : m_soundfxvalues ) { + soundfxrecord.second.owner( Owner ); + } +} + float TGauge::GetScaledValue() const { return false == m_interpolatescale ? m_value * m_scale + m_offset : m_value * std::lerp(m_scale, m_endscale, std::clamp(m_value / m_endvalue, 0.f, 1.f)) + m_offset; diff --git a/vehicle/Gauge.h b/vehicle/Gauge.h index bf0f83c3..160330d6 100644 --- a/vehicle/Gauge.h +++ b/vehicle/Gauge.h @@ -55,6 +55,10 @@ public: void AssignBool(bool *bValue); void UpdateValue(); void AssignState( bool const *State ); + // sounds of a control defined outside the cab section of the .mmd file (vehicle graph): Increase and Decrease in the + // format of soundinc: and sounddec:, Positions as the soundN: entries of a control block (e.g. "sound0: a.wav sound1: b.wav"). + // to take their place in the cab from the submodel, assign them before Init() + void AssignSounds( std::string const &Increase, std::string const &Decrease, std::string const &Positions, TDynamicObject const *Owner ); // returns offset of submodel associated with the button from the model centre glm::vec3 model_offset() const; TGaugeType type() const; diff --git a/vehicle/Train.cpp b/vehicle/Train.cpp index a77c5749..ea25cdb3 100644 --- a/vehicle/Train.cpp +++ b/vehicle/Train.cpp @@ -186,6 +186,12 @@ TGauge &TCab::Gauge(int n) } }; +void TCab::ClearControls() +{ + ggList.clear(); + btList.clear(); +} + TButton &TCab::Button(int n) { // pobranie adresu obiektu animowanego wyborem 1 z 2 /* @@ -919,6 +925,11 @@ std::shared_ptr TTrain::GetTrainState(dictionary_source const dict->insert("air_temperature", Global.AirTemperature); dict->insert("light_level", Global.fLuminance - std::max(0.f, Global.Overcast - 1.f)); + if (mvOccupied->Graph) + { + export_graph_values(*dict); + } + return dict; } @@ -8532,6 +8543,11 @@ bool TTrain::Update(double const Deltatime) while (simulation::Commands.pop(commanddata, static_cast(command_target::vehicle) | id())) { + if (mvOccupied->Graph && graph_command(commanddata)) + { + // the command operated a control of the vehicle graph + continue; + } auto lookup = m_commandhandlers.find(commanddata.command); if (lookup != m_commandhandlers.end()) { @@ -8899,7 +8915,8 @@ bool TTrain::Update(double const Deltatime) ggClockHInd.Update(); } - Cabine[iCabn].Update(lowvoltagepower); // nowy sposób ustawienia animacji + // lamps of a graph vehicle are powered as its graph says + Cabine[iCabn].Update(mvOccupied->Graph ? true : lowvoltagepower); // nowy sposób ustawienia animacji /* if (ggZbS.SubModel) { @@ -10387,8 +10404,9 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName) parser >> token; auto lookup{internalsounds.find(token)}; - if (lookup == internalsounds.end()) + if (lookup == internalsounds.end() || mvOccupied->Graph) { + // sounds of a graph vehicle are defined by its graph continue; } @@ -10782,6 +10800,13 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) } } + if (mvOccupied->Graph) + { + // controls and indicators of a graph vehicle come from its graph; before the model of the cab is initialised, + // so the animated submodels are known + initialize_graph_controls(cabindex); + } + if (DynamicObject->mdKabina) DynamicObject->mdKabina->Init(); // obrócenie modelu oraz optymalizacja, również zapisanie binarnego diff --git a/vehicle/Train.h b/vehicle/Train.h index 7627bbf8..06b8fb38 100644 --- a/vehicle/Train.h +++ b/vehicle/Train.h @@ -22,6 +22,11 @@ http://mozilla.org/MPL/2.0/. #undef snprintf // pyint.h->python +namespace vgraph +{ +class cab_switch; +} // namespace vgraph + // typedef enum {st_Off, st_Starting, st_On, st_ShuttingDown} T4State; const double fCzuwakBlink = 0.15; @@ -38,6 +43,8 @@ public: void Update( bool const Power ); TGauge &Gauge( int n = -1 ); // pobranie adresu obiektu TButton &Button( int n = -1 ); // pobranie adresu obiektu + // removes the controls and indicators read from the .mmd, which graph vehicles don't use + void ClearControls(); // members glm::vec3 CabPos1{0, 1, 1}; glm::vec3 CabPos2{0, 1, -1}; @@ -193,6 +200,12 @@ class TTrain { bool initialize_gauge(cParser &Parser, std::string const &Label, int const Cabindex); // initializes a button matching provided label. returns: true if the label was found, false otherwise bool initialize_button(cParser &Parser, std::string const &Label, int const Cabindex); + // vehicle graph: creates the controls and indicators of the cab from the cab blocks of the graph, in place of the ones of the .mmd + void initialize_graph_controls(int const Cabindex); + // vehicle graph: passes the command to the controls of the graph. returns: true if a control took it + bool graph_command(command_data const &Command); + // vehicle graph: adds the values of the fb.export blocks to the state for the python screens + void export_graph_values(dictionary_source &Dictionary) const; // helper, returns true for EMU with oerlikon brake bool is_eztoer() const; // locates nearest vehicle belonging to the consist @@ -520,6 +533,12 @@ class TTrain { int m_linebreakerstate { 0 }; // 0: open, 1: closed, 2: freshly closed (and yes this is awful way to go about it) static const commandhandler_map m_commandhandlers; control_mapper m_controlmapper; + // vehicle graph: controls operated by the commands of the simulator (command, key_inc, key_dec parameters of cab.switch) + struct graph_command_binding { + vgraph::cab_switch *control; + int operation; // 0: press/release, 1: increase, -1: decrease + }; + std::unordered_map> m_graphcommands; public: // reszta może by?publiczna diff --git a/vehicle/graph/blocks/cab.cpp b/vehicle/graph/blocks/cab.cpp index 94e1d816..aaa78079 100644 --- a/vehicle/graph/blocks/cab.cpp +++ b/vehicle/graph/blocks/cab.cpp @@ -43,6 +43,18 @@ std::vector split_labels(std::string const &Text) } // namespace +int cab_of(block_setup const &Setup) +{ + auto const index{find_param(Setup.param_defs, "cab")}; + if (index < 0) + { + return 0; + } + // "any", "1", "2", "0" (the engine room) + auto const &value{Setup.params[index].text}; + return value == "1" ? 1 : value == "2" ? 2 : value == "0" ? -1 : 0; +} + // cab_switch cab_switch::cab_switch(block_setup const &Setup) @@ -50,7 +62,10 @@ cab_switch::cab_switch(block_setup const &Setup) m_positioncount(std::max(1, static_cast(std::lround(number(Setup, param::positions))))), m_min(number(Setup, param::min)), m_max(number(Setup, param::max)), m_spring(boolean(Setup, param::spring)), m_labels(split_labels(text(Setup, param::labels))), m_animation(static_cast(choice(Setup, param::animation))), m_step(number(Setup, param::step)), m_offset(number(Setup, param::offset)), m_smoothing(number(Setup, param::smoothing)), m_submodel(text(Setup, param::submodel)), - m_keyincrease(text(Setup, param::key_inc)), m_keydecrease(text(Setup, param::key_dec)), m_command(text(Setup, param::command)), m_hardwarename(text(Setup, param::hw_name)) + m_soundincrease(text(Setup, param::sound_inc)), m_sounddecrease(text(Setup, param::sound_dec)), + m_soundpositions(text(Setup, param::sound_positions)), + m_keyincrease(text(Setup, param::key_inc)), m_keydecrease(text(Setup, param::key_dec)), m_command(text(Setup, param::command)), m_hardwarename(text(Setup, param::hw_name)), + m_cab(cab_of(Setup)) { // the default is given as a value, as the author sees it; the switch starts at the position closest to it auto const defaultvalue{number(Setup, param::default_value)}; @@ -59,6 +74,7 @@ cab_switch::cab_switch(block_setup const &Setup) m_defaultposition = clamp_position(static_cast(std::lround((defaultvalue - m_min) / (m_max - m_min) * (m_positioncount - 1)))); } m_position = m_defaultposition; + m_display = static_cast(m_position); } int cab_switch::clamp_position(int const Position) const @@ -84,6 +100,7 @@ void cab_switch::update(frame &Frame) { set(Frame, port::value, value_at(m_position)); set(Frame, port::index, m_position); + m_display = static_cast(m_position); } void cab_switch::increase() @@ -181,10 +198,14 @@ block_type cab_switch::describe() number_param("offset", 0.0, "deg/mm"), number_param("smoothing", 0.2, "s"), text_param("submodel"), + text_param("sound_inc"), + text_param("sound_dec"), + text_param("sound_positions"), text_param("key_inc"), text_param("key_dec"), text_param("command"), text_param("hw_name"), + cab_param(), }; type.create = make_block; return type; @@ -194,7 +215,8 @@ block_type cab_switch::describe() cab_lamp::cab_lamp(block_setup const &Setup) : block(Setup), m_supplied(is_connected(Setup, port::supply)), m_blink(boolean(Setup, param::blink)), m_blinkperiod(std::max(0.01, number(Setup, param::blink_period))), - m_minvoltage(number(Setup, param::min_voltage)), m_power(std::max(0.0, number(Setup, param::power))), m_submodel(text(Setup, param::submodel)) + m_minvoltage(number(Setup, param::min_voltage)), m_power(std::max(0.0, number(Setup, param::power))), m_submodel(text(Setup, param::submodel)), + m_soundon(text(Setup, param::sound_on)), m_soundoff(text(Setup, param::sound_off)), m_cab(cab_of(Setup)) { } @@ -233,6 +255,9 @@ block_type cab_lamp::describe() number_param("min_voltage", 70.0, "V"), number_param("power", 2.0, "W"), text_param("submodel"), + text_param("sound_on"), + text_param("sound_off"), + cab_param(), }; type.create = make_block; return type; @@ -243,7 +268,7 @@ block_type cab_lamp::describe() cab_gauge::cab_gauge(block_setup const &Setup, bool const Twoneedles) : block(Setup), m_twoneedles(Twoneedles), m_min(number(Setup, param::min)), m_max(number(Setup, param::max)), m_anglemin(number(Setup, param::angle_min)), m_anglemax(number(Setup, param::angle_max)), m_smoothing(number(Setup, param::smoothing)), m_submodel(text(Setup, param::submodel)), - m_submodel2(Twoneedles ? text(Setup, param::submodel2) : std::string{}) + m_submodel2(Twoneedles ? text(Setup, param::submodel2) : std::string{}), m_cab(cab_of(Setup)) { m_values[0] = m_values[1] = static_cast(std::min(m_min, m_max)); } @@ -292,6 +317,7 @@ block_type cab_gauge::describe(bool const Twoneedles) { type.params.push_back(text_param("submodel2")); } + type.params.push_back(cab_param()); if (Twoneedles) { type.create = [](block_setup const &Setup) -> std::unique_ptr { return std::make_unique(Setup, true); }; @@ -308,7 +334,7 @@ block_type cab_gauge::describe(bool const Twoneedles) vehicle_sound::vehicle_sound(block_setup const &Setup) : block(Setup), m_file(text(Setup, param::file)), m_basevolume(number(Setup, param::volume)), m_basepitch(number(Setup, param::pitch)), m_loop(boolean(Setup, param::loop)), m_placement(static_cast(choice(Setup, param::placement))), m_range(number(Setup, param::range)), - m_offset{number(Setup, param::offset_x), number(Setup, param::offset_y), number(Setup, param::offset_z)} + m_offset{number(Setup, param::offset_x), number(Setup, param::offset_y), number(Setup, param::offset_z)}, m_cab(cab_of(Setup)) { } @@ -364,11 +390,35 @@ block_type vehicle_sound::describe() number_param("offset_x", 0.0, "m"), number_param("offset_y", 0.0, "m"), number_param("offset_z", 0.0, "m"), + cab_param(), }; type.create = make_block; return type; } +// cab_export + +cab_export::cab_export(block_setup const &Setup) : block(Setup), m_name(text(Setup, 0)), m_kind(static_cast(choice(Setup, 1))) {} + +void cab_export::update(frame &Frame) +{ + m_value = get(Frame, port::value); +} + +block_type cab_export::describe() +{ + block_type type; + type.id = "fb.export"; + type.category = "indicators"; + type.name = "Screen value"; + type.description = "Publishes the value for the python screens of the cab, as an entry of the train state with the given name."; + type.origin = block_origin::cab; + type.ports = {signal_in("value", value_type::real)}; + type.params = {text_param("name"), choice_param("kind", {"float", "int", "bool"}, "float")}; + type.create = make_block; + return type; +} + void register_cab_blocks(catalog &Catalog) { Catalog.add(cab_switch::describe()); @@ -376,6 +426,7 @@ void register_cab_blocks(catalog &Catalog) Catalog.add(cab_gauge::describe(false)); Catalog.add(cab_gauge::describe(true)); Catalog.add(vehicle_sound::describe()); + Catalog.add(cab_export::describe()); } } // namespace vgraph diff --git a/vehicle/graph/blocks/cab.h b/vehicle/graph/blocks/cab.h index 4dab7758..00c24bbb 100644 --- a/vehicle/graph/blocks/cab.h +++ b/vehicle/graph/blocks/cab.h @@ -48,10 +48,14 @@ public: offset, smoothing, submodel, + sound_inc, + sound_dec, + sound_positions, key_inc, key_dec, command, - hw_name + hw_name, + cab }; }; enum class behaviour_type @@ -136,6 +140,21 @@ public: { return m_submodel; } + // sounds played by the animated submodel when the position increases or decreases, in the format of + // soundinc: and sounddec: of the .mmd controls + std::string const &sound_increase() const + { + return m_soundincrease; + } + std::string const &sound_decrease() const + { + return m_sounddecrease; + } + // sounds of single positions, as soundN: entries of the .mmd controls with N the number of the position + std::string const &sound_positions() const + { + return m_soundpositions; + } // bindings: keys, existing simulator command, name for the hardware desk std::string const &key_increase() const { @@ -153,6 +172,16 @@ public: { return m_hardwarename; } + // cab whose model holds the submodel: 0 any, 1 or 2 + int cab() const + { + return m_cab; + } + // stable address of the value animating the submodel (the position), for TGauge::AssignFloat + float *display_state() + { + return &m_display; + } static block_type describe(); @@ -171,11 +200,16 @@ private: double const m_offset; double const m_smoothing; std::string const m_submodel; + std::string const m_soundincrease; + std::string const m_sounddecrease; + std::string const m_soundpositions; std::string const m_keyincrease; std::string const m_keydecrease; std::string const m_command; std::string const m_hardwarename; + int const m_cab; int m_position{0}; + float m_display{0.f}; }; // indicator lamp. when the graph simulates low voltage circuits and the supply input is connected, the lamp lights @@ -200,7 +234,10 @@ public: blink_period, min_voltage, power, - submodel + submodel, + sound_on, + sound_off, + cab }; }; @@ -229,6 +266,19 @@ public: { return m_submodel; } + // sounds played when the lamp lights and goes out, as soundinc: and sounddec: of the .mmd lamps + std::string const &sound_on() const + { + return m_soundon; + } + std::string const &sound_off() const + { + return m_soundoff; + } + int cab() const + { + return m_cab; + } static block_type describe(); @@ -239,9 +289,15 @@ private: double const m_minvoltage; double const m_power; std::string const m_submodel; + std::string const m_soundon; + std::string const m_soundoff; + int const m_cab; bool m_lit{false}; }; +// cab given by the "cab" parameter of a cab block: 0 any, 1 or 2, -1 the engine room +int cab_of(block_setup const &Setup); + // needle gauge with one or two needles: voltmeter, ammeter, speedometer, pressure gauge class cab_gauge final : public block { @@ -318,6 +374,10 @@ public: { return Needle == 0 ? m_submodel : m_submodel2; } + int cab() const + { + return m_cab; + } static block_type describe(bool Twoneedles); @@ -330,6 +390,7 @@ private: double const m_smoothing; std::string const m_submodel; std::string const m_submodel2; + int const m_cab; float m_values[2]{0.f, 0.f}; }; @@ -358,7 +419,8 @@ public: range, offset_x, offset_y, - offset_z + offset_z, + cab }; }; // the same placements as sound_placement of the audio subsystem @@ -422,6 +484,11 @@ public: { return m_offset[2]; } + // for the sounds placed in the cab: the cab they belong to, 0 any + int cab() const + { + return m_cab; + } static block_type describe(); @@ -433,9 +500,53 @@ private: placement_type const m_placement; double const m_range; double const m_offset[3]; + int const m_cab; bool m_playing{false}; float m_volume{0.f}; float m_pitch{1.f}; }; +// value published for the python screens of the cab (TTrain::GetTrainState), under the specified name +class cab_export final : public block +{ +public: + struct port + { + enum : std::size_t + { + value + }; + }; + enum class kind_type + { + real, + integer, + boolean + }; + + explicit cab_export(block_setup const &Setup); + + void update(frame &Frame) override; + + std::string const &name() const + { + return m_name; + } + kind_type kind() const + { + return m_kind; + } + double value() const + { + return m_value; + } + + static block_type describe(); + +private: + std::string const m_name; + kind_type const m_kind; + double m_value{0.0}; +}; + } // namespace vgraph diff --git a/vehicle/graph/blocks/diesel.cpp b/vehicle/graph/blocks/diesel.cpp new file mode 100644 index 00000000..400f8e41 --- /dev/null +++ b/vehicle/graph/blocks/diesel.cpp @@ -0,0 +1,920 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/graph_body.h" +#include "McZapkie/MOVER.h" + +#include + +// combustion engine drive: engine -> clutch and gearbox / hydraulic transmission / main generator. +// +// the mechanical connections keep the convention of the electric drives: torque forward, rotational speed back. the +// engine is a torque source like the electric motors: it reads the speed of its shaft from the back channel and gives the +// torque. the speed of the engine shaft is a state of the block connected to the engine (drive.gearbox, drive.hydro or +// drive.generator), the same way the speed of the wheels is a state of the vehicle body behind drive.axle: that block +// holds the inertia of the engine side (crankshaft, flywheel, generator rotor), integrates the engine speed with the engine +// torque and the torque of its coupling (slipping clutch, torque converter, generator load), and returns it to the engine. +// when the coupling is locked (clutch engaged without slip, lockup clutch of the converter) the engine follows the speed +// of the wheels, as in TMoverParameters::dizel_Momentum, so the chain is rigid and stays stable at any step. +// the engine shaft has to be connected to exactly one such block; its speed is in rad/s, positive, in the frame of the +// engine. the direction of the vehicle is set after the coupling (dir inputs of the gearbox and the hydraulic transmission) +namespace vgraph +{ + +namespace +{ + +double constexpr two_pi{2.0 * M_PI}; +double constexpr rpm_to_rad{two_pi / 60.0}; + +TMoverParameters &vehicle(frame const &Frame) +{ + return *Frame.body->mover; +} + +block_type make_type(std::string Id, std::string Name, std::string Description, block_origin Origin) +{ + block_type type; + type.id = std::move(Id); + type.category = "drive"; + type.name = std::move(Name); + type.description = std::move(Description); + type.origin = Origin; + return type; +} + +// direction of the drive in the frame of the vehicle: -1, 0 or 1 (as for the electric motors) +int absolute_direction(frame const &Frame, double const Dir) +{ + auto const dir{Dir > 0.5 ? 1 : Dir < -0.5 ? -1 : 0}; + auto const cab{Frame.body->cab}; + return dir * (cab != 0 ? cab : 1); +} + +double sign(double const Value) +{ + return Value > 0.0 ? 1.0 : Value < 0.0 ? -1.0 : 0.0; +} + +// clamp which accepts the bounds in any order (safe_clamp of the Mover) +double clamp_any(double const Value, double const Bound1, double const Bound2) +{ + return std::clamp(Value, std::min(Bound1, Bound2), std::max(Bound1, Bound2)); +} + +// linear interpolation in the rows of a table (first column the argument, sorted); the end rows outside of it +double interpolate(std::vector> const &Rows, std::size_t const Column, double const Argument) +{ + if (Rows.empty()) + { + return 0.0; + } + if (Argument <= Rows.front()[0]) + { + return Rows.front()[Column]; + } + for (std::size_t idx = 1; idx < Rows.size(); ++idx) + { + auto const &upper{Rows[idx]}; + if (Argument <= upper[0]) + { + auto const &lower{Rows[idx - 1]}; + auto const span{upper[0] - lower[0]}; + return span > 0.0 ? lower[Column] + (upper[Column] - lower[Column]) * (Argument - lower[0]) / span : upper[Column]; + } + } + return Rows.back()[Column]; +} + +// rows of a table parameter with at least the specified number of columns, sorted by the first one +std::vector> sorted_rows(std::vector> const &Rows, std::size_t const Columns) +{ + std::vector> result; + for (auto const &row : Rows) + { + if (row.size() >= Columns) + { + result.push_back(row); + } + } + std::stable_sort(std::begin(result), std::end(result), [](auto const &Left, auto const &Right) { return Left[0] < Right[0]; }); + return result; +} + +// combustion engine with a speed governor and a starter (adapted from TMoverParameters::dizel_Momentum, dizel_fillcheck +// and dizel_Update) +class drive_diesel final : public block +{ +public: + struct port + { + enum : std::size_t + { + start, + stop, + fuel, + demand, + shaft, + running, + starting, + rpm, + torque, + fill, + power, + fuel_rate + }; + }; + struct param + { + enum : std::size_t + { + idle_rpm, + max_rpm, + governor, + droop, + fill_rate, + curve, + curve_type, + m_max, + rpm_m_max, + m_nmax, + friction, + starter_torque, + start_time, + fuel_consumption, + main_engine + }; + }; + + explicit drive_diesel(block_setup const &Setup) + : block(Setup), m_idle(std::max(1.0, number(Setup, param::idle_rpm))), m_max(std::max(m_idle + 1.0, number(Setup, param::max_rpm))), + m_allspeed(choice(Setup, param::governor) == 0), m_band(std::max(1.0, std::max(0.001, number(Setup, param::droop)) * m_max)), + m_fillrate(std::max(0.0, number(Setup, param::fill_rate))), m_curve(sorted_rows(table(Setup, param::curve), 2)), m_curvepower(choice(Setup, param::curve_type) == 1), + m_mmax(number(Setup, param::m_max)), m_nmmax(number(Setup, param::rpm_m_max)), m_mnmax(number(Setup, param::m_nmax)), m_friction(std::max(0.0, number(Setup, param::friction))), + m_startertorque(std::max(0.0, number(Setup, param::starter_torque))), m_starttime(std::max(0.0, number(Setup, param::start_time))), + m_mainengine(boolean(Setup, param::main_engine)) + { + if (m_curve.size() < 2) + { + m_curve.clear(); + } + if (m_nmmax == m_max) + { + // as in LoadFIZ_DList: guards the division in the torque parabola + m_nmmax = m_max - 1.0; + } + // fuel used per hour at the full power, as dizel_FuelConsumption of LoadFIZ_DList: g/kWh * kW / (850 g/l) + auto const ratedpower{std::max(0.0, full_torque(m_max) - m_friction) * m_max * rpm_to_rad / 1000.0}; + m_fuelperhour = std::max(0.0, number(Setup, param::fuel_consumption)) * ratedpower / 850.0; + } + + void update(frame &Frame) override + { + auto const omega{back_out(Frame, port::shaft)}; + auto const rpm{omega / rpm_to_rad}; + auto const start{get_bool(Frame, port::start)}; + auto const stop{get_bool(Frame, port::stop)}; + auto const fuel{get_bool(Frame, port::fuel)}; + auto const startpressed{start && false == m_startprevious}; + m_startprevious = start; + + // start procedure (dizel_Update): the starter cranks the engine for start_time, then the ignition; the starter + // keeps helping until the engine reaches its idle speed (dizel_spinup) + if (false == m_running && false == m_cranking && startpressed && fuel && false == stop) + { + m_cranking = true; + m_cranktime = 0.0; + } + if (m_cranking) + { + if (stop || false == fuel) + { + m_cranking = false; + } + else + { + m_cranktime += Frame.dt; + if (m_cranktime >= m_starttime) + { + m_cranking = false; + m_running = true; + m_spinup = true; + } + } + } + if (m_running && (stop || false == fuel)) + { + // knocked out without fuel, as dizel_Update does when the fuel pump stops + m_running = false; + } + m_spinup = m_spinup && m_running && rpm < 0.95 * m_idle; + if (m_running && false == m_spinup && rpm < 0.25 * m_idle) + { + // stalled, e.g. by engaging the clutch at standstill + m_running = false; + } + + // fuel injection set by the governor (dizel_fillcheck), reached at fill_rate (dizel_Update: fillspeed) + auto target{0.0}; + if (m_running) + { + auto const demand{std::clamp(get(Frame, port::demand), 0.0, 1.0)}; + if (m_allspeed) + { + // all speed governor: the demand sets the speed between the idle and the maximum one + auto const setspeed{m_idle + demand * (m_max - m_idle)}; + target = std::clamp((setspeed - rpm) / m_band, 0.0, 1.0); + } + else + { + // the demand is the injection; the governor keeps the idle speed and cuts the fuel at the maximum one + target = std::max(demand, std::clamp((m_idle - rpm) / m_band, 0.0, 1.0)); + target = std::min(target, std::clamp((m_max - rpm) / m_band, 0.0, 1.0)); + } + } + m_fill += (target - m_fill) * std::min(1.0, m_fillrate * Frame.dt); + + // torque (dizel_Momentum): the curve at full injection times the injection, less the motion resistance of + // the engine (dizel_Mstand), which acts against the rotation and fades out at standstill + m_combustion = rpm > 0.0 ? full_torque(rpm) * m_fill : 0.0; + m_resistance = m_friction * std::clamp(rpm / (0.1 * m_idle), -1.0, 1.0); + auto starter{0.0}; + if (m_cranking || m_spinup) + { + // starter, falling with the speed as in dizel_Momentum + starter = m_startertorque * 0.3 / (0.3 + std::max(0.0, rpm / m_idle)); + } + m_torque = m_combustion - m_resistance + starter; + m_omega = omega; + m_netpower = (m_combustion - m_resistance) * omega / 1000.0; + m_fuelrate = m_running ? m_fuelperhour * std::max(0.0, rpm / m_max) * m_fill : 0.0; + m_fuelused += m_fuelrate * Frame.dt / 3600.0; + + set_forward(Frame, port::shaft, m_torque); + set_bool(Frame, port::running, m_running); + set_bool(Frame, port::starting, m_cranking || m_spinup); + set(Frame, port::rpm, rpm); + set(Frame, port::torque, m_torque); + set(Frame, port::fill, m_fill); + set(Frame, port::power, m_netpower); + set(Frame, port::fuel_rate, m_fuelrate); + } + + // the fields of the vehicle body are written after all blocks were updated, so the engine speed replaces the + // revolutions written by the traction motors of a diesel-electric vehicle (for the diesel vehicles enrot is the + // engine speed: engine sounds, cab indicators) + void late_update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + // for the diesel vehicles Mains is the running engine; with more engines (or line breakers) any of them + body.Mains = Frame.body->mains.any(Frame.step, m_running); + if (false == m_mainengine) + { + return; + } + body.enrot = m_omega / two_pi; + body.dizel_fill = m_fill; + body.Mm = m_combustion - m_resistance; + body.dizel_Torque = m_torque; + body.dizel_Power = m_netpower; + // as in TractionForce: (dizel_Mstand + Mm) * enrot * 2pi / 1000 + body.EnginePower = m_combustion * m_omega / 1000.0; + body.dizel_startup = m_cranking; + body.dizel_spinup = m_spinup; + body.dizel_FuelConsumptionActual = m_fuelrate; + body.dizel_FuelConsumptedTotal = m_fuelused; + // speeds the sounds and indicators scale with (EngineIdleRPM, EngineMaxRPM, EngineRPMRatio), rev/s + body.dizel_nmin = m_idle / 60.0; + body.dizel_nmax = m_max / 60.0; + body.nmax = m_max / 60.0; + } + + std::size_t state_count() const override + { + return 1; + } + char const *state_name(std::size_t) const override + { + return "fuel_used"; + } + double state_value(std::size_t) const override + { + return m_fuelused; + } + + static block_type describe() + { + auto type{make_type("drive.diesel", "Diesel engine", + "Combustion engine with a speed governor and a starter (dizel_Momentum). start begins the start procedure: the starter cranks the engine for " + "start_time, then the fuel is injected. demand 0..1 sets the speed (all_speed governor) or the injection (min_max governor, idle and maximum " + "speed kept). The torque is the curve at full injection (table rpm -> Nm or kW, or the Mover parabola m_max, rpm_m_max, m_nmax) times the " + "injection, less the friction. The shaft has to go to one drive.gearbox, drive.hydro or drive.generator, which holds the engine speed.", + block_origin::extracted)}; + type.ports = {signal_in("start", value_type::boolean), + signal_in("stop", value_type::boolean, 0.0, "", true), + signal_in("fuel", value_type::boolean, 1.0, "", true), + signal_in("demand", value_type::real), + physical_out("shaft", domain::mechanical), + signal_out("running", value_type::boolean), + signal_out("starting", value_type::boolean), + signal_out("rpm", value_type::real, "rpm"), + signal_out("torque", value_type::real, "Nm"), + signal_out("fill", value_type::real), + signal_out("power", value_type::real, "kW"), + signal_out("fuel_rate", value_type::real, "l/h")}; + type.params = {number_param("idle_rpm", 600.0, "rpm"), + number_param("max_rpm", 1500.0, "rpm"), + choice_param("governor", {"all_speed", "min_max"}, "all_speed"), + number_param("droop", 0.05), + number_param("fill_rate", 10.0, "1/s"), + table_param("curve", {"rpm", "torque"}), + choice_param("curve_type", {"torque", "power"}, "torque"), + number_param("m_max", 3000.0, "Nm"), + number_param("rpm_m_max", 1100.0, "rpm"), + number_param("m_nmax", 2600.0, "Nm"), + number_param("friction", 300.0, "Nm"), + number_param("starter_torque", 600.0, "Nm"), + number_param("start_time", 2.0, "s"), + number_param("fuel_consumption", 220.0, "g/kWh"), + boolean_param("main_engine", true)}; + type.late = true; + type.create = make_block; + return type; + } + +private: + // torque at full injection, Nm + double full_torque(double const Rpm) const + { + if (false == m_curve.empty()) + { + auto const value{interpolate(m_curve, 1, Rpm)}; + // power curve in kW: torque = P / omega, the speed held at the first row to keep it finite + return m_curvepower ? value * 1000.0 / (std::max(Rpm, std::max(1.0, m_curve.front()[0])) * rpm_to_rad) : value; + } + // parabola of dizel_Momentum: Mmax at nMmax, Mnmax at nmax + auto const x{(Rpm - m_nmmax) / (m_nmmax - m_max)}; + return std::max(0.0, m_mmax - (m_mmax - m_mnmax) * x * x); + } + + double const m_idle; // rpm + double const m_max; // rpm + bool const m_allspeed; + double const m_band; // rpm + double const m_fillrate; + std::vector> m_curve; + bool const m_curvepower; + double const m_mmax; + double m_nmmax; + double const m_mnmax; + double const m_friction; + double const m_startertorque; + double const m_starttime; + bool const m_mainengine; + double m_fuelperhour{0.0}; // l/h at full power and maximum speed + bool m_startprevious{false}; + bool m_cranking{false}; + bool m_running{false}; + bool m_spinup{false}; + double m_cranktime{0.0}; + double m_fill{0.0}; + double m_combustion{0.0}; + double m_resistance{0.0}; + double m_torque{0.0}; + double m_omega{0.0}; + double m_netpower{0.0}; + double m_fuelrate{0.0}; + double m_fuelused{0.0}; +}; + +// friction clutch and mechanical gearbox (adapted from TMoverParameters::dizel_Momentum without the converter, +// dizel_EngageChange and dizel_AutoGearCheck). holds the speed of the engine: slipping, the clutch passes its friction +// torque and the engine speed follows its own torque; synchronous, the engine follows the gearbox input and its whole +// torque passes on +class drive_gearbox final : public block +{ +public: + struct port + { + enum : std::size_t + { + in, + gear, + clutch, + dir, + out, + actual, + engage, + slip, + shifting + }; + }; + struct param + { + enum : std::size_t + { + gears, + clutch_torque, + engage_rate, + release_rate, + auto_clutch, + inertia, + eff + }; + }; + + explicit drive_gearbox(block_setup const &Setup) + : block(Setup), m_clutchtorque(std::max(0.0, number(Setup, param::clutch_torque))), m_engagerate(std::max(0.0, number(Setup, param::engage_rate))), + m_releaserate(std::max(0.0, number(Setup, param::release_rate))), m_autoclutch(boolean(Setup, param::auto_clutch)), + m_inertia(std::max(0.01, number(Setup, param::inertia))), m_efficiency(std::clamp(number(Setup, param::eff), 0.01, 1.0)) + { + for (auto const &row : table(Setup, param::gears)) + { + if (row.size() >= 2) + { + m_gears.emplace_back(static_cast(std::lround(row[0])), row[1]); + } + } + } + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const moment{forward_in(Frame, port::in)}; + auto const requestedgear{static_cast(std::lround(get(Frame, port::gear)))}; + auto const requesteddir{absolute_direction(Frame, get(Frame, port::dir))}; + + // a change of the gear or the direction waits for the released clutch; with auto_clutch the gearbox releases it + m_shifting = (requestedgear != m_gear || requesteddir != m_dir); + auto const demand{std::clamp(get(Frame, port::clutch), 0.0, 1.0)}; + m_engagestate = (m_shifting && m_autoclutch) ? 0.0 : demand; + // dizel_EngageChange: the pressure approaches the set one, and jumps to it when close + if (std::abs(m_engage - m_engagestate) < 0.11) + { + m_engage = m_engagestate; + } + else + { + m_engage += (m_engage > m_engagestate ? m_releaserate : m_engagerate) * Frame.dt * (m_engagestate - m_engage); + } + if (m_shifting && m_engage < 0.1) + { + m_gear = requestedgear; + m_dir = requesteddir; + m_shifting = false; + } + + // ratio from the engine to the output shaft, in the frame of the vehicle; 0 in neutral + auto ratio{0.0}; + for (auto const &gear : m_gears) + { + if (gear.first == m_gear && m_gear != 0) + { + ratio = gear.second * m_dir; + break; + } + } + + auto gearmoment{0.0}; + m_deltaomega = 0.0; + m_slipping = false; + if (ratio == 0.0) + { + // neutral: the input shaft turns with the engine, which runs free + auto const newomega{m_omega + moment / m_inertia * Frame.dt}; + m_omega = (newomega < 0.0 && m_omega >= 0.0) ? 0.0 : newomega; + m_previous = m_omega; + } + else + { + // dizel_Momentum, in rad/s and kg m2 (the .fiz AIM is the inertia times 2pi) + auto const n{back_out(Frame, port::out) * ratio}; // speed of the gearbox input + auto const torquec{m_clutchtorque * m_engage}; + if (std::abs(moment) > torquec || std::abs(m_previous - m_omega) > 0.1 * two_pi) + { + // slipping, because of the speed difference or the torque above the friction of the clutch + m_slipping = true; + m_deltaomega = m_omega - m_previous; + gearmoment = torquec * sign(m_deltaomega); + auto const eps{(moment - gearmoment) / m_inertia}; + auto newomega{m_omega + eps * Frame.dt}; + if ((newomega - n) * (m_omega - m_previous) < 0.0 && torquec > 0.1) + { + // the slip changed its sign: the clutch grips + newomega = n; + } + if (newomega * m_omega <= 0.0 && eps * m_omega < 0.0) + { + // no passing through zero speed + newomega = 0.0; + } + m_omega = newomega; + } + else + { + // synchronous: the engine follows the gearbox, within what the clutch can hold + gearmoment = moment; + m_omega = clamp_any(n, m_omega + (moment - torquec) / m_inertia * Frame.dt, m_omega + (moment + torquec) / m_inertia * Frame.dt); + } + m_previous = n; + } + + set_forward(Frame, port::out, gearmoment * ratio * m_efficiency); + add_back(Frame, port::in, m_omega); + set(Frame, port::actual, m_gear); + set(Frame, port::engage, m_engage); + set_bool(Frame, port::slip, m_slipping && m_engage > 0.1 && std::abs(m_deltaomega) > 0.2 * two_pi); + set_bool(Frame, port::shifting, m_shifting); + // clutch state for the sounds and the cab indicators (rsEngageSlippery, clutch gauges) + body.dizel_engage = m_engage; + body.dizel_engagestate = m_engagestate; + body.dizel_engagedeltaomega = m_deltaomega / two_pi; + body.dizel_automaticgearstatus = m_shifting ? sign(requestedgear - m_gear) : 0.0; + } + + static block_type describe() + { + auto type{make_type("drive.gearbox", "Gearbox with clutch", + "Friction clutch and mechanical gearbox (dizel_Momentum, dizel_EngageChange). gear selects the row of the gears table (gear number, ratio; " + "0 or a missing number is neutral, reverse gears can have negative ratios), dir reverses. A change of the gear or the direction waits until " + "the clutch is released; with auto_clutch the gearbox releases it itself. Holds the speed of the engine (inertia of the engine side).", + block_origin::extracted)}; + type.ports = {physical_in("in", domain::mechanical), + signal_in("gear", value_type::integer, 1.0, "", true), + signal_in("clutch", value_type::real, 1.0, "", true), + signal_in("dir", value_type::real, 1.0, "", true), + physical_out("out", domain::mechanical), + signal_out("actual", value_type::integer), + signal_out("engage", value_type::real), + signal_out("slip", value_type::boolean), + signal_out("shifting", value_type::boolean)}; + type.params = {table_param("gears", {"gear", "ratio"}), + number_param("clutch_torque", 1500.0, "Nm"), + number_param("engage_rate", 0.5, "1/s"), + number_param("release_rate", 0.9, "1/s"), + boolean_param("auto_clutch", true), + number_param("inertia", 10.0, "kg m2"), + number_param("eff", 0.95)}; + type.create = make_block; + return type; + } + +private: + double const m_clutchtorque; + double const m_engagerate; + double const m_releaserate; + bool const m_autoclutch; + double const m_inertia; + double const m_efficiency; + std::vector> m_gears; + int m_gear{0}; + int m_dir{0}; + bool m_shifting{false}; + bool m_slipping{false}; + double m_engage{0.0}; + double m_engagestate{0.0}; + double m_omega{0.0}; // engine speed, rad/s + double m_previous{0.0}; // gearbox input speed in the previous step (dizel_n_old) + double m_deltaomega{0.0}; +}; + +// hydraulic transmission: torque converter with filling, fluid coupling range and lockup clutch (adapted from the hydro_TC +// part of TMoverParameters::dizel_Momentum). holds the speed of the engine like the gearbox +class drive_hydro final : public block +{ +public: + struct port + { + enum : std::size_t + { + in, + power, + running, + dir, + out, + fill, + lockup, + ratio + }; + }; + struct param + { + enum : std::size_t + { + tc_tmmax, + tc_cp, + tc_table, + tc_tii, + tc_tio, + tc_too, + fill_rate, + drain_rate, + lockup_torque, + lockup_speed, + unlock_speed, + idle_rpm, + ans_speed, + inertia + }; + }; + + explicit drive_hydro(block_setup const &Setup) + : block(Setup), m_tmmax(number(Setup, param::tc_tmmax)), m_couplingpoint(std::clamp(number(Setup, param::tc_cp), 0.01, 0.99)), + m_table(sorted_rows(table(Setup, param::tc_table), 2)), m_torqueinin(number(Setup, param::tc_tii)), m_torqueinout(number(Setup, param::tc_tio)), + m_torqueoutout(number(Setup, param::tc_too)), m_fillrate(std::max(0.0, number(Setup, param::fill_rate))), m_drainrate(std::max(0.0, number(Setup, param::drain_rate))), + m_lockuptorque(std::max(0.0, number(Setup, param::lockup_torque))), m_lockupspeed(number(Setup, param::lockup_speed)), m_unlockspeed(number(Setup, param::unlock_speed)), + m_idle(std::max(1.0, number(Setup, param::idle_rpm)) * rpm_to_rad), m_ansspeed(number(Setup, param::ans_speed)), + m_inertia(std::max(0.01, number(Setup, param::inertia))) + { + if (m_table.size() < 2) + { + m_table.clear(); + } + } + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const moment{forward_in(Frame, port::in)}; + auto const dir{absolute_direction(Frame, get(Frame, port::dir))}; + auto const power{get_bool(Frame, port::power)}; + // the engine running (Mains of dizel_Momentum); a stopped engine drains the converter and releases the lockup clutch + auto const running{get_bool(Frame, port::running)}; + auto const vel{std::abs(Frame.body->velocity) * 3.6}; + // speed of the output shaft in the frame of the engine; without a direction it turns with the engine + auto const n{dir != 0 ? back_out(Frame, port::out) * dir : m_omega}; + + // filling and draining of the converter, engaging and releasing of the lockup clutch + if (power && running && m_omega > 0.9 * m_idle) + { + m_fill += m_fillrate * Frame.dt; + } + if ((false == power && vel < m_ansspeed) || false == running || m_omega < 0.8 * m_idle) + { + m_fill -= m_drainrate * Frame.dt; + } + m_fill = std::clamp(m_fill, 0.0, 1.0); + if (vel > m_lockupspeed && running && m_omega > 0.9 * m_idle && power) + { + m_lockup = true; + m_lockuprate += m_fillrate * Frame.dt; + } + if (vel < (power ? m_lockupspeed : m_unlockspeed) || false == running || m_omega < 0.8 * m_idle) + { + m_lockup = false; + m_lockuprate -= m_drainrate * Frame.dt; + } + m_lockuprate = std::clamp(m_lockuprate, 0.0, 1.0); + + // torques of the converter; its constants are for the speeds in rev/s, as in the .fiz files + auto const nin{m_omega / two_pi}; + auto const nout{m_previous / two_pi}; + auto const hydrotorque{nin * nin * m_torqueinin + (nin - nout) * m_torqueinout + nout * nout * m_torqueoutout}; + auto const speedratio{nout / std::max(0.01, nin)}; + double torquein; + double torqueout; + if (false == m_table.empty()) + { + m_tmratio = interpolate(m_table, 1, speedratio); + torqueout = hydrotorque * m_fill * m_tmratio; + torquein = hydrotorque * m_fill * std::min(1.0, m_tmratio); + } + else if (speedratio < m_couplingpoint) + { + // converter range: the torque is multiplied + auto const x{1.0 - speedratio / m_couplingpoint}; + m_tmratio = 1.0 + (m_tmmax - 1.0) * x * x; + torquein = hydrotorque * m_fill; + torqueout = hydrotorque * m_fill * m_tmratio; + } + else + { + // fluid coupling range + m_tmratio = (1.0 - speedratio) / (1.0 - m_couplingpoint); + torquein = hydrotorque * m_fill * m_tmratio; + torqueout = torquein; + } + auto const torquelock{m_lockuptorque * m_lockuprate}; + + // the main clutch of dizel_Momentum is taken as always engaged; the converter is drained to disconnect the engine + double gearmoment; + auto const capacity{torquelock + std::abs(torquein)}; + if (std::abs(moment) > capacity || std::abs(m_previous - m_omega) > 0.1 * two_pi) + { + auto const deltaomega{m_omega - m_previous}; + // NOTE: dizel_Momentum adds the lockup torque without the sign of the slip; here it acts against the slip too + gearmoment = (torquelock + std::abs(torqueout)) * sign(deltaomega); + auto const eps{(moment - capacity * sign(deltaomega)) / m_inertia}; + auto newomega{m_omega + eps * Frame.dt}; + if ((newomega - n) * deltaomega < 0.0 && torquelock > 0.1) + { + // the lockup clutch grips + newomega = n; + } + if (newomega * m_omega <= 0.0 && eps * m_omega < 0.0) + { + newomega = 0.0; + } + m_omega = newomega; + } + else + { + gearmoment = moment; + m_omega = clamp_any(n, m_omega + (moment - capacity) / m_inertia * Frame.dt, m_omega + (moment + capacity) / m_inertia * Frame.dt); + } + m_previous = n; + + set_forward(Frame, port::out, gearmoment * dir); + add_back(Frame, port::in, m_omega); + set(Frame, port::fill, m_fill); + set_bool(Frame, port::lockup, m_lockup); + // the ratio of an empty converter means nothing (and grows without limit when the engine stops) + set(Frame, port::ratio, m_fill > 0.0 ? m_tmratio : 0.0); + body.hydro_TC_Fill = m_fill; + body.hydro_TC_Lockup = m_lockup; + body.hydro_TC_LockupRate = m_lockuprate; + body.hydro_TC_TMRatio = m_tmratio; + body.hydro_TC_TorqueIn = torquein; + body.hydro_TC_TorqueOut = gearmoment; + body.hydro_TC_nIn = nin; + body.hydro_TC_nOut = nout; + } + + static block_type describe() + { + auto type{make_type("drive.hydro", "Hydraulic transmission", + "Torque converter with filling, fluid coupling range above the coupling point and a lockup clutch, as hydro_TC of the .fiz files (dizel_Momentum). " + "power fills the converter (with the engine above 0.9 of idle_rpm); without it the converter drains below ans_speed. The lockup clutch engages " + "above lockup_speed with power and releases below it (below unlock_speed without power). running (the engine's) false drains the converter and " + "releases the lockup clutch. Holds the speed of the engine (inertia of the engine side).", + block_origin::extracted)}; + type.ports = {physical_in("in", domain::mechanical), + signal_in("power", value_type::boolean), + signal_in("running", value_type::boolean, 1.0, "", true), + signal_in("dir", value_type::real, 1.0, "", true), + physical_out("out", domain::mechanical), + signal_out("fill", value_type::real), + signal_out("lockup", value_type::boolean), + signal_out("ratio", value_type::real)}; + type.params = {number_param("tc_tmmax", 2.0), + number_param("tc_cp", 0.85), + table_param("tc_table", {"speed_ratio", "torque_ratio"}), + number_param("tc_tii", 4.5), + number_param("tc_tio", 0.0), + number_param("tc_too", 0.0), + number_param("fill_rate", 1.0, "1/s"), + number_param("drain_rate", 1.0, "1/s"), + number_param("lockup_torque", 3000.0, "Nm"), + number_param("lockup_speed", 40.0, "km/h"), + number_param("unlock_speed", 30.0, "km/h"), + number_param("idle_rpm", 600.0, "rpm"), + number_param("ans_speed", 3.0, "km/h"), + number_param("inertia", 10.0, "kg m2")}; + type.create = make_block; + return type; + } + +private: + double const m_tmmax; + double const m_couplingpoint; + std::vector> m_table; + double const m_torqueinin; + double const m_torqueinout; + double const m_torqueoutout; + double const m_fillrate; + double const m_drainrate; + double const m_lockuptorque; + double const m_lockupspeed; + double const m_unlockspeed; + double const m_idle; // rad/s + double const m_ansspeed; + double const m_inertia; + double m_fill{0.0}; + bool m_lockup{false}; + double m_lockuprate{0.0}; + double m_tmratio{1.0}; + double m_omega{0.0}; // engine speed, rad/s + double m_previous{0.0}; // output speed in the previous step, rad/s +}; + +// main generator of a diesel-electric vehicle: separately excited dc generator (adapted from the DieselElectric part of +// TMoverParameters::TractionForce: the ellipse of the generator characteristic and the power available at the engine +// speed). holds the speed of the engine +class drive_generator final : public block +{ +public: + struct port + { + enum : std::size_t + { + in, + excitation, + out, + u, + i, + p + }; + }; + struct param + { + enum : std::size_t + { + u_max, + i_max, + p_max, + nominal_rpm, + eff, + field_time, + inertia + }; + }; + + explicit drive_generator(block_setup const &Setup) + : block(Setup), m_umax(std::max(0.0, number(Setup, param::u_max))), m_imax(std::max(1.0, number(Setup, param::i_max))), + m_pmax(std::max(0.0, number(Setup, param::p_max)) * 1000.0), m_nominal(std::max(1.0, number(Setup, param::nominal_rpm)) * rpm_to_rad), + m_efficiency(std::clamp(number(Setup, param::eff), 0.01, 1.0)), m_fieldtime(std::max(0.0, number(Setup, param::field_time))), + m_inertia(std::max(0.01, number(Setup, param::inertia))) + { + } + + void update(frame &Frame) override + { + auto const moment{forward_in(Frame, port::in)}; + auto const current{back_out(Frame, port::out)}; // drawn by the load in the previous step + auto const excitation{std::clamp(get(Frame, port::excitation), 0.0, 1.0)}; + auto const speedratio{std::clamp(m_omega / m_nominal, 0.0, 1.0)}; + // the voltage at no load grows with the excitation and the speed; under load it follows the ellipse of the + // separately excited generator (zero at i_max) and the hyperbola of the power available at this speed + auto const noload{m_umax * excitation * speedratio}; + auto const load{std::min(1.0, std::abs(current) / m_imax)}; + auto target{noload * std::sqrt(std::max(0.0, 1.0 - load * load))}; + if (std::abs(current) > 1.0) + { + target = std::min(target, m_pmax * speedratio / std::abs(current)); + } + // the field of the generator follows with its time constant, which also keeps the loop with the motors stable + m_voltage += (target - m_voltage) * (m_fieldtime > 0.0 ? std::min(1.0, Frame.dt / m_fieldtime) : 1.0); + m_power = m_voltage * current; + // load of the engine: the electric power and the losses + auto const loadtorque{m_power / m_efficiency / std::max(m_omega, 1.0)}; + m_omega = std::max(0.0, m_omega + (moment - loadtorque) / m_inertia * Frame.dt); + + set_forward(Frame, port::out, m_voltage); + add_back(Frame, port::in, m_omega); + set(Frame, port::u, m_voltage); + set(Frame, port::i, current); + set(Frame, port::p, m_power / 1000.0); + } + + static block_type describe() + { + auto type{make_type("drive.generator", "Main generator", + "Main generator of a diesel-electric vehicle. The voltage at no load is u_max times the excitation (0..1), proportional to the engine speed " + "below nominal_rpm; under load it falls on an ellipse to zero at i_max, and the power is limited to p_max scaled with the engine speed " + "(DieselElectric of TractionForce). The electric power loads the engine. Holds the speed of the engine (inertia of the engine and the rotor).", + block_origin::extracted)}; + type.ports = {physical_in("in", domain::mechanical), signal_in("excitation", value_type::real), physical_out("out", domain::high_voltage), + signal_out("u", value_type::real, "V"), signal_out("i", value_type::real, "A"), signal_out("p", value_type::real, "kW")}; + type.params = {number_param("u_max", 750.0, "V"), + number_param("i_max", 2400.0, "A"), + number_param("p_max", 330.0, "kW"), + number_param("nominal_rpm", 1500.0, "rpm"), + number_param("eff", 0.93), + number_param("field_time", 0.5, "s"), + number_param("inertia", 20.0, "kg m2")}; + type.create = make_block; + return type; + } + +private: + double const m_umax; + double const m_imax; + double const m_pmax; // W + double const m_nominal; // rad/s + double const m_efficiency; + double const m_fieldtime; + double const m_inertia; + double m_voltage{0.0}; + double m_power{0.0}; + double m_omega{0.0}; // engine speed, rad/s +}; + +} // namespace + +void register_diesel_blocks(catalog &Catalog) +{ + Catalog.add(drive_diesel::describe()); + Catalog.add(drive_gearbox::describe()); + Catalog.add(drive_hydro::describe()); + Catalog.add(drive_generator::describe()); +} + +} // namespace vgraph diff --git a/vehicle/graph/blocks/drive.cpp b/vehicle/graph/blocks/drive.cpp new file mode 100644 index 00000000..e2059fe4 --- /dev/null +++ b/vehicle/graph/blocks/drive.cpp @@ -0,0 +1,658 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/graph_body.h" +#include "McZapkie/MOVER.h" + +#include + +// drive chain: motor -> gear -> axle. the mechanical connections pass the torque forward and the rotational speed back. +// both are in the frame of the vehicle: positive torque pushes the vehicle towards its front (cab 1), positive speed +// is the rotation when moving that way. the dir input of the motors is the position of the reverser in the occupied cab, +// so the motors turn it to the frame of the vehicle with the occupied cab +namespace vgraph +{ + +namespace +{ + +double constexpr two_pi{2.0 * M_PI}; + +TMoverParameters &vehicle(frame const &Frame) +{ + return *Frame.body->mover; +} + +block_type make_type(std::string Id, std::string Name, std::string Description, block_origin Origin) +{ + block_type type; + type.id = std::move(Id); + type.category = "drive"; + type.name = std::move(Name); + type.description = std::move(Description); + type.origin = Origin; + return type; +} + +// direction of the drive in the frame of the vehicle: -1, 0 or 1 +int absolute_direction(frame const &Frame, double const Dir) +{ + auto const dir{Dir > 0.5 ? 1 : Dir < -0.5 ? -1 : 0}; + auto const cab{Frame.body->cab}; + return dir * (cab != 0 ? cab : 1); +} + +// generic motor with a torque and power limit; a stand-in for the drives not extracted yet +class drive_simple final : public block +{ +public: + struct port + { + enum : std::size_t + { + wn, + demand, + dir, + shaft, + i, + p + }; + }; + struct param + { + enum : std::size_t + { + t_max, + p_max, + eff, + min_voltage + }; + }; + + explicit drive_simple(block_setup const &Setup) + : block(Setup), m_supplied(is_connected(Setup, port::wn)), m_tmax(number(Setup, param::t_max)), m_pmax(number(Setup, param::p_max)), + m_efficiency(std::clamp(number(Setup, param::eff), 0.01, 1.0)), m_minvoltage(number(Setup, param::min_voltage)) + { + } + + void update(frame &Frame) override + { + auto const voltage{m_supplied ? forward_in(Frame, port::wn) : 0.0}; + auto const powered{false == m_supplied || (voltage > 0.0 && voltage >= m_minvoltage)}; + auto const dir{absolute_direction(Frame, get(Frame, port::dir))}; + auto const demand{std::clamp(get(Frame, port::demand), -1.0, 1.0)}; + auto const speed{back_out(Frame, port::shaft)}; // rad/s + auto torque{0.0}; + if (powered && dir != 0) + { + if (demand >= 0.0) + { + // traction, limited by the power above the base speed + auto const limit{std::abs(speed) > 1e-3 ? std::min(m_tmax, m_pmax / std::abs(speed)) : m_tmax}; + torque = dir * demand * limit; + } + else + { + // electric brake, against the rotation + auto const limit{std::abs(speed) > 1e-3 ? std::min(m_tmax, m_pmax / std::abs(speed)) : 0.0}; + torque = (speed >= 0.0 ? -1.0 : 1.0) * (-demand) * limit * std::min(1.0, std::abs(speed)); + } + } + auto const mechanical{torque * speed}; // W, positive when driving + auto const electrical{mechanical >= 0.0 ? mechanical / m_efficiency : mechanical * m_efficiency}; + auto const current{m_supplied && voltage > 1.0 ? electrical / voltage : 0.0}; + if (m_supplied) + { + add_back(Frame, port::wn, current); + } + set_forward(Frame, port::shaft, torque); + set(Frame, port::i, current); + set(Frame, port::p, electrical); + auto &body{vehicle(Frame)}; + body.Im = current; + body.EngineVoltage = voltage * dir; + body.enrot = speed / two_pi; + } + + static block_type describe() + { + auto type{make_type("drive.simple", "Simple motor", + "Motor with a torque limit (t_max) and a power limit (p_max): demand 0..1 drives, -1..0 brakes electrically. A stand-in for prototypes.", + block_origin::native)}; + type.ports = {physical_in("wn", domain::high_voltage, true), signal_in("demand", value_type::real), signal_in("dir", value_type::real), + physical_out("shaft", domain::mechanical), signal_out("i", value_type::real, "A"), signal_out("p", value_type::real, "W")}; + type.params = {number_param("t_max", 5000.0, "Nm"), number_param("p_max", 500000.0, "W"), number_param("eff", 0.9), number_param("min_voltage", 0.0, "V")}; + type.create = make_block; + return type; + } + +private: + bool const m_supplied; + double const m_tmax; + double const m_pmax; + double const m_efficiency; + double const m_minvoltage; +}; + +class drive_gear final : public block +{ +public: + struct port + { + enum : std::size_t + { + in, + out + }; + }; + + explicit drive_gear(block_setup const &Setup) : block(Setup), m_ratio(number(Setup, 0)), m_efficiency(number(Setup, 1)) {} + + void update(frame &Frame) override + { + set_forward(Frame, port::out, forward_in(Frame, port::in) * m_ratio * m_efficiency); + add_back(Frame, port::in, back_out(Frame, port::out) * m_ratio); + } + + static block_type describe() + { + auto type{make_type("drive.gear", "Gear", "Transmission: torque times ratio and efficiency, speed of the input times ratio. ratio = wheel teeth / motor teeth.", + block_origin::extracted)}; + type.ports = {physical_in("in", domain::mechanical), physical_out("out", domain::mechanical)}; + type.params = {number_param("ratio", 1.0), number_param("eff", 1.0)}; + type.create = make_block; + return type; + } + +private: + double const m_ratio; + double const m_efficiency; +}; + +// wheelsets driven by one shaft: the torque becomes the tractive force of the vehicle +class drive_axle final : public block +{ +public: + struct port + { + enum : std::size_t + { + shaft, + force, + slip + }; + }; + + explicit drive_axle(block_setup const &Setup) : block(Setup), m_axles(std::max(0.0, number(Setup, 0))) {} + + void update(frame &Frame) override + { + auto const &body{vehicle(Frame)}; + auto const diameter{body.WheelDiameter > 0.0 ? body.WheelDiameter : 1.0}; + auto const force{forward_in(Frame, port::shaft) * 2.0 / diameter * m_axles}; + Frame.body->tractive_force += force; + add_back(Frame, port::shaft, Frame.body->wheel_speed * two_pi); + set(Frame, port::force, force); + set_bool(Frame, port::slip, Frame.body->slipping); + } + + static block_type describe() + { + auto type{make_type("drive.axle", "Driven axles", + "Driven wheelsets: the torque of one shaft (of one motor, after the gear) times powered_axles gives the force at the wheel rim. All axle forces are " + "summed into the tractive force of the vehicle.", + block_origin::native)}; + type.ports = {physical_in("shaft", domain::mechanical), signal_out("force", value_type::real, "N"), signal_out("slip", value_type::boolean)}; + type.params = {number_param("powered_axles", 1.0)}; + type.create = make_block; + return type; + } + +private: + double const m_axles; +}; + +// series dc traction motors with resistor starting (TMoverParameters::Current, Momentum) +class drive_dc_series final : public block +{ +public: + struct port + { + enum : std::size_t + { + wn, + position, + shunt, + dir, + high, + reset, + shaft, + i, + u_motor, + overload + }; + }; + struct param + { + enum : std::size_t + { + rlist, + mpt, + volt, + winding_res, + circuit_res, + imax_lo, + imax_hi, + imin_lo, + imin_hi, + trip_delay + }; + }; + + explicit drive_dc_series(block_setup const &Setup) + : block(Setup), m_windingres(number(Setup, param::winding_res)), m_circuitres(number(Setup, param::circuit_res)), m_imaxlo(number(Setup, param::imax_lo)), + m_imaxhi(number(Setup, param::imax_hi)), m_tripdelay(number(Setup, param::trip_delay)) + { + for (auto const &row : table(Setup, param::rlist)) + { + // pos, r, bn, mn, auto + scheme entry; + entry.r = row[1]; + entry.bn = static_cast(std::lround(row[2])); + entry.mn = static_cast(std::lround(row[3])); + m_schemes.push_back(entry); + } + for (auto const &row : table(Setup, param::mpt)) + { + // pos, mfi, mIsat, mfi0, fi, Isat, fi0 + TMotorParameters motor; + motor.mfi = row[1]; + motor.mIsat = row[2]; + motor.mfi0 = row[3]; + motor.fi = row[4]; + motor.Isat = row[5]; + motor.fi0 = row[6]; + m_motors.push_back(motor); + } + if (m_schemes.empty()) + { + m_schemes.emplace_back(); + } + if (m_motors.empty()) + { + m_motors.emplace_back(); + } + } + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const dir{absolute_direction(Frame, get(Frame, port::dir))}; + auto const voltage{forward_in(Frame, port::wn) * dir}; + auto const position{std::clamp(static_cast(std::lround(get(Frame, port::position))), 0, static_cast(m_schemes.size()) - 1)}; + auto const shunt{std::clamp(static_cast(std::lround(get(Frame, port::shunt))), 0, static_cast(m_motors.size()) - 1)}; + auto const &scheme{m_schemes[position]}; + auto const &motor{m_motors[shunt]}; + auto const revolutions{back_out(Frame, port::shaft) / two_pi}; // rev/s + + if (get_bool(Frame, port::reset)) + { + m_overload = false; + } + auto current{0.0}; + if (scheme.bn != 0 && std::abs(voltage) > 1.0 && false == m_overload) + { + auto const resistance{scheme.r + m_circuitres}; + current = series_motor_current(voltage, revolutions, scheme.mn, scheme.mn * m_windingres + resistance, motor); + } + // overload relay, as in TractionForce: trips when the current stays above the limit + auto const imax{get_bool(Frame, port::high) ? m_imaxhi : m_imaxlo}; + if (imax > 0.0 && std::abs(current) > imax) + { + m_overtime += Frame.dt; + if (m_overtime > m_tripdelay) + { + m_overload = true; + } + } + else + { + m_overtime = 0.0; + } + auto const torque{series_motor_momentum(current, motor)}; + auto const linecurrent{current * scheme.bn}; + add_back(Frame, port::wn, std::abs(linecurrent)); + set_forward(Frame, port::shaft, torque); + set(Frame, port::i, current); + set(Frame, port::u_motor, scheme.mn > 0 ? (std::abs(voltage) - std::abs(current) * (scheme.r + m_circuitres)) / scheme.mn : 0.0); + set_bool(Frame, port::overload, m_overload); + body.Im = current; + body.EngineVoltage = voltage; + body.enrot = revolutions; + body.MainCtrlActualPos = position; + } + + std::size_t state_count() const override + { + return 1; + } + char const *state_name(std::size_t) const override + { + return "overload"; + } + double state_value(std::size_t) const override + { + return m_overload ? 1.0 : 0.0; + } + + static block_type describe() + { + auto type{make_type("drive.dc_series", "Series DC motors", + "Series wound DC traction motors with resistor starting, computed as for the .fiz vehicles. position selects the row of rlist (resistance, " + "branches, motors in series), shunt the row of mpt (motor characteristics). The shaft carries the torque of one motor. high selects imax_hi " + "for the overload relay; reset clears it.", + block_origin::extracted)}; + type.ports = {physical_in("wn", domain::high_voltage), + signal_in("position", value_type::integer), + signal_in("shunt", value_type::integer, 0.0, "", true), + signal_in("dir", value_type::real), + signal_in("high", value_type::boolean, 0.0, "", true), + signal_in("reset", value_type::boolean, 0.0, "", true), + physical_out("shaft", domain::mechanical), + signal_out("i", value_type::real, "A"), + signal_out("u_motor", value_type::real, "V"), + signal_out("overload", value_type::boolean)}; + type.params = {table_param("rlist", {"pos", "r", "bn", "mn", "auto"}), + table_param("mpt", {"pos", "mfi", "misat", "mfi0", "fi", "isat", "fi0"}), + number_param("volt", 1500.0, "V"), + number_param("winding_res", 0.0, "ohm"), + number_param("circuit_res", 0.0, "ohm"), + number_param("imax_lo", 0.0, "A"), + number_param("imax_hi", 0.0, "A"), + number_param("imin_lo", 0.0, "A"), + number_param("imin_hi", 0.0, "A"), + number_param("trip_delay", 0.05, "s")}; + type.create = make_block; + return type; + } + +private: + struct scheme + { + double r{0.0}; + int bn{0}; + int mn{0}; + }; + + double const m_windingres; + double const m_circuitres; + double const m_imaxlo; + double const m_imaxhi; + double const m_tripdelay; + std::vector m_schemes; + std::vector m_motors; + double m_overtime{0.0}; + bool m_overload{false}; +}; + +// induction motors fed by inverters (the ElectricInductionMotor drive of TMoverParameters::TractionForce) +class drive_async final : public block +{ +public: + struct port + { + enum : std::size_t + { + wn, + demand, + dir, + shaft, + i, + f, + ed_active, + ed + }; + }; + // in the order of the eimc constants they're copied to + struct param + { + enum : std::size_t + { + dfic, + dfmax, + p, + cfu, + cim, + icif, + uzmax, + uzh, + du, + i0, + fcfu, + fcfuh, + f0, + a1, + pmax, + fh, + ph, + vh0, + vh1, + imax, + abed, + edep, + flat, + motors, + ratio, + eff, + log_force + }; + }; + + explicit drive_async(block_setup const &Setup) + : block(Setup), m_flat(boolean(Setup, param::flat)), m_motors(static_cast(std::lround(number(Setup, param::motors)))), m_ratio(number(Setup, param::ratio)), + m_efficiency(number(Setup, param::eff)), m_logforce(boolean(Setup, param::log_force)) + { + std::fill(std::begin(m_eimc), std::end(m_eimc), 0.0); + std::fill(std::begin(m_eimv), std::end(m_eimv), 0.0); + m_eimc[eimc_s_dfic] = number(Setup, param::dfic); + m_eimc[eimc_s_dfmax] = number(Setup, param::dfmax); + m_eimc[eimc_s_p] = number(Setup, param::p); + m_eimc[eimc_s_cfu] = number(Setup, param::cfu); + m_eimc[eimc_s_cim] = number(Setup, param::cim); + m_eimc[eimc_s_icif] = number(Setup, param::icif); + m_eimc[eimc_f_Uzmax] = number(Setup, param::uzmax); + m_eimc[eimc_f_Uzh] = number(Setup, param::uzh); + m_eimc[eimc_f_DU] = number(Setup, param::du); + m_eimc[eimc_f_I0] = number(Setup, param::i0); + m_eimc[eimc_f_cfu] = number(Setup, param::fcfu); + // as in LoadFIZ_Engine: fcfuH defaults to fcfu + m_eimc[eimc_f_cfuH] = number(Setup, param::fcfuh) > 0.0 ? number(Setup, param::fcfuh) : m_eimc[eimc_f_cfu]; + m_eimc[eimc_p_F0] = number(Setup, param::f0); + m_eimc[eimc_p_a1] = number(Setup, param::a1); + m_eimc[eimc_p_Pmax] = number(Setup, param::pmax); + m_eimc[eimc_p_Fh] = number(Setup, param::fh); + m_eimc[eimc_p_Ph] = number(Setup, param::ph); + m_eimc[eimc_p_Vh0] = number(Setup, param::vh0); + m_eimc[eimc_p_Vh1] = number(Setup, param::vh1); + m_eimc[eimc_p_Imax] = number(Setup, param::imax); + m_eimc[eimc_p_abed] = number(Setup, param::abed); + m_eimc[eimc_p_eped] = number(Setup, param::edep); + } + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const voltage{forward_in(Frame, port::wn)}; + auto const dir{absolute_direction(Frame, get(Frame, port::dir))}; + auto const enrot{back_out(Frame, port::shaft) / two_pi}; + auto const demand{std::clamp(get(Frame, port::demand), -1.0, 1.0)}; + if (voltage < 1.0 || dir == 0) + { + // without supply the inverters stop, as for the vehicles without the main circuit (Mains) + std::fill(std::begin(m_eimv), std::end(m_eimv), 0.0); + m_eimvpr = 0.0; + m_dynamicbrake = false; + add_back(Frame, port::wn, 0.0); + publish(Frame, body, 0.0, 0.0, 0.0, voltage * dir, enrot); + return; + } + auto const vel{body.Vel}; + // set value of the force and the rate of its ramp, as in TractionForce + double posratio; + double rate; + if (demand < -0.001) + { + m_dynamicbrake = true; + auto const vhramp{std::clamp((vel - m_eimc[eimc_p_Vh0]) / std::max(0.001, m_eimc[eimc_p_Vh1] - m_eimc[eimc_p_Vh0]), 0.0, 1.0)}; + posratio = demand * vhramp; + rate = 5.0; + } + else + { + m_dynamicbrake = false; + posratio = std::max(demand, 0.0); + if (m_flat && m_eimc[eimc_p_F0] * m_eimv[eimv_Fful] > 0) + { + posratio = std::min(posratio * m_eimc[eimc_p_F0] / m_eimv[eimv_Fful], 1.0); + } + rate = 4.0; + } + if (body.SlippingWheels) + { + // anti-slip: the set value drops quickly + posratio = 0.0; + rate = 10.0; + } + induction_motor_inputs input; + input.demand = posratio; + input.rate = rate; + input.dynamic_brake = m_dynamicbrake; + input.engine_voltage = voltage; + input.enrot = enrot; + input.vel = vel; + input.v = body.V; + input.dir_absolute = dir; + input.dir_active = dir; + input.powered_axles = m_motors; + input.inverters_ratio = 1.0; + input.ratio = m_ratio; + input.efficiency = m_efficiency; + input.wheel_diameter = body.WheelDiameter > 0.0 ? body.WheelDiameter : 1.0; + input.log_force = m_logforce; + input.max_voltage = body.EnginePowerSource.CollectorParameters.MaxV; + input.dt = Frame.dt; + induction_motor_outputs output; + induction_motor_update(input, m_eimc, m_eimv, m_eimvpr, m_vadd, output); + + add_back(Frame, port::wn, output.Itot); + // share of the requested braking force the motors deliver, for the blending with the pneumatic brake + auto edshare{0.0}; + if (m_dynamicbrake && m_eimc[eimc_p_Fh] > 0.0 && demand < -0.001) + { + edshare = std::clamp(std::abs(m_eimv[eimv_Fr]) / (m_eimc[eimc_p_Fh] * -demand), 0.0, 1.0); + } + publish(Frame, body, output.Mm, output.Im, edshare, voltage, enrot); + } + + std::size_t state_count() const override + { + return 2; + } + char const *state_name(std::size_t Index) const override + { + return Index == 0 ? "set_value" : "force_kn"; + } + double state_value(std::size_t Index) const override + { + return Index == 0 ? m_eimvpr : m_eimv[eimv_Fr]; + } + + static block_type describe() + { + auto type{make_type("drive.async", "Induction motors with inverters", + "Induction traction motors fed by inverters, computed as for the .fiz vehicles (Engine: ElectricInductionMotor). demand 0..1 drives, -1..0 brakes " + "electrically (faded out between vh1 and vh0). The shaft carries the torque of one motor; ratio and eff have to match the gear, and motors the " + "driven axles. ed is the share of the requested braking force the motors deliver, for the ed input of the distributor.", + block_origin::extracted)}; + type.ports = {physical_in("wn", domain::high_voltage), signal_in("demand", value_type::real), signal_in("dir", value_type::real), + physical_out("shaft", domain::mechanical), signal_out("i", value_type::real, "A"), signal_out("f", value_type::real, "Hz"), + signal_out("ed_active", value_type::boolean), signal_out("ed", value_type::real)}; + type.params = {number_param("dfic", 0.0), + number_param("dfmax", 0.0), + number_param("p", 2.0), + number_param("cfu", 0.0), + number_param("cim", 0.0), + number_param("icif", 0.0), + number_param("uzmax", 0.0, "V"), + number_param("uzh", 0.0, "V"), + number_param("du", 0.0, "V"), + number_param("i0", 0.0, "A"), + number_param("fcfu", 0.0), + number_param("fcfuh", 0.0), + number_param("f0", 0.0, "kN"), + number_param("a1", 0.0), + number_param("pmax", 0.0, "kW"), + number_param("fh", 0.0, "kN"), + number_param("ph", 0.0, "kW"), + number_param("vh0", 0.0, "km/h"), + number_param("vh1", 0.0, "km/h"), + number_param("imax", 0.0, "A"), + number_param("abed", 1.0), + number_param("edep", 1.5), + boolean_param("flat", false), + number_param("motors", 4.0), + number_param("ratio", 1.0), + number_param("eff", 1.0), + boolean_param("log_force", false)}; + type.create = make_block; + return type; + } + +private: + void publish(frame &Frame, TMoverParameters &Body, double const Torque, double const Current, double const Edshare, double const Voltage, double const Revolutions) + { + set_forward(Frame, port::shaft, Torque); + set(Frame, port::i, Current); + set(Frame, port::f, std::abs(m_eimv[eimv_fp])); + set_bool(Frame, port::ed_active, m_dynamicbrake); + set(Frame, port::ed, Edshare); + Body.Im = Current; + Body.EngineVoltage = Voltage; + Body.enrot = Revolutions; + Body.DynamicBrakeFlag = m_dynamicbrake; + Body.InverterFrequency = std::abs(m_eimv[eimv_fp]); + std::copy(std::begin(m_eimv), std::end(m_eimv), Body.eimv); + } + + bool const m_flat; + int const m_motors; + double const m_ratio; + double const m_efficiency; + bool const m_logforce; + double m_eimc[26]; + double m_eimv[21]; + double m_eimvpr{0.0}; + double m_vadd{0.0}; + bool m_dynamicbrake{false}; +}; + +} // namespace + +void register_drive_blocks(catalog &Catalog) +{ + Catalog.add(drive_simple::describe()); + Catalog.add(drive_dc_series::describe()); + Catalog.add(drive_async::describe()); + Catalog.add(drive_gear::describe()); + Catalog.add(drive_axle::describe()); +} + +} // namespace vgraph diff --git a/vehicle/graph/blocks/electric.cpp b/vehicle/graph/blocks/electric.cpp new file mode 100644 index 00000000..7e23c731 --- /dev/null +++ b/vehicle/graph/blocks/electric.cpp @@ -0,0 +1,658 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/graph_body.h" +#include "McZapkie/MOVER.h" + +#include + +// high voltage circuit and auxiliary supplies. voltage goes forward through the connections, current comes back; the +// back channel lags one step, so a breaker sees the current its load drew in the previous step +namespace vgraph +{ + +namespace +{ + +TMoverParameters &vehicle(frame const &Frame) +{ + return *Frame.body->mover; +} + +block_type make_type(std::string Id, std::string Name, std::string Description, block_origin Origin) +{ + block_type type; + type.id = std::move(Id); + type.category = "electric"; + type.name = std::move(Name); + type.description = std::move(Description); + type.origin = Origin; + return type; +} + +// current collector. in the simulator its contact with the catenary is the pantograph of the vehicle model +class el_pantograph final : public block +{ +public: + struct port + { + enum : std::size_t + { + raise, + air, + wn, + up, + height, + u + }; + }; + struct param + { + enum : std::size_t + { + index, + raise_time, + lower_time, + min_pressure, + preview_voltage + }; + }; + + explicit el_pantograph(block_setup const &Setup) + : block(Setup), m_air(is_connected(Setup, port::air)), m_index(std::clamp(static_cast(std::lround(number(Setup, param::index))), 0, 1)), + m_raisetime(std::max(0.01, number(Setup, param::raise_time))), m_lowertime(std::max(0.01, number(Setup, param::lower_time))), + m_minpressure(number(Setup, param::min_pressure)), m_previewvoltage(number(Setup, param::preview_voltage)) + { + } + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const pressure{m_air ? forward_in(Frame, port::air) : body.EnginePowerSource.CollectorParameters.MaxPress}; + auto const raise{get_bool(Frame, port::raise) && pressure >= m_minpressure}; + auto &pantograph{body.Pantographs[m_index]}; + pantograph.is_active = raise; + // the model raises the pantograph at a speed depending on this pressure + body.PantPress = pressure; + m_height = std::clamp(m_height + (raise ? Frame.dt / m_raisetime : -Frame.dt / m_lowertime), 0.0, 1.0); + double voltage; + if (Frame.body->standalone) + { + voltage = m_height >= 1.0 ? m_previewvoltage : 0.0; + } + else + { + voltage = std::abs(m_index == 0 ? body.PantFrontVolt : body.PantRearVolt); + } + pantograph.voltage = voltage; + set_forward(Frame, port::wn, voltage); + // current drawn by the vehicle through this pantograph, in the previous step + Frame.body->current += back_out(Frame, port::wn); + set_bool(Frame, port::up, m_height >= 1.0); + set(Frame, port::height, m_height); + set(Frame, port::u, voltage); + } + + std::size_t state_count() const override + { + return 1; + } + char const *state_name(std::size_t) const override + { + return "height"; + } + double state_value(std::size_t) const override + { + return m_height; + } + + static block_type describe() + { + auto type{make_type("el.pantograph", "Pantograph", + "Current collector: index 0 is the front pantograph of the vehicle model, 1 the rear one. Raising needs the air pressure (when connected). " + "Without the simulator the catenary has the preview voltage.", + block_origin::extracted)}; + type.ports = {signal_in("raise", value_type::boolean), physical_in("air", domain::air, true), physical_out("wn", domain::high_voltage), + signal_out("up", value_type::boolean), signal_out("height", value_type::real), signal_out("u", value_type::real, "V")}; + type.params = {number_param("index", 0.0), number_param("raise_time", 4.0, "s"), number_param("lower_time", 1.0, "s"), number_param("min_pressure", 3.5, "bar"), + number_param("preview_voltage", 3000.0, "V")}; + type.create = make_block; + return type; + } + +private: + bool const m_air; + int const m_index; + double const m_raisetime; + double const m_lowertime; + double const m_minpressure; + double const m_previewvoltage; + double m_height{0.0}; +}; + +// joins two supplies: the higher voltage wins, the current goes back to the source(s) supplying it +class el_junction final : public block +{ +public: + struct port + { + enum : std::size_t + { + a, + b, + out + }; + }; + + explicit el_junction(block_setup const &Setup) : block(Setup) {} + + void update(frame &Frame) override + { + auto const a{forward_in(Frame, port::a)}; + auto const b{forward_in(Frame, port::b)}; + set_forward(Frame, port::out, std::max(a, b)); + auto const current{back_out(Frame, port::out)}; + if (std::abs(a - b) < 1.0) + { + add_back(Frame, port::a, 0.5 * current); + add_back(Frame, port::b, 0.5 * current); + } + else + { + add_back(Frame, a > b ? port::a : port::b, current); + } + } + + static block_type describe() + { + auto type{make_type("el.junction", "Junction", "Joins two high voltage supplies, e.g. two pantographs.", block_origin::primitive)}; + type.ports = {physical_in("a", domain::high_voltage), physical_in("b", domain::high_voltage, true), physical_out("out", domain::high_voltage)}; + type.create = make_block; + return type; + } +}; + +// line breaker (WS). its state is the Mains of the vehicle body, which the sounds and the AI watch; with more breakers +// (multi-system vehicles) the Mains is closed when any of them is +class el_hsb final : public block +{ +public: + struct port + { + enum : std::size_t + { + in, + close, + open, + hold, + out, + closed, + i, + tripped + }; + }; + struct param + { + enum : std::size_t + { + trip_current, + close_time, + min_voltage, + init_time + }; + }; + + explicit el_hsb(block_setup const &Setup) + : block(Setup), m_tripcurrent(number(Setup, param::trip_current)), m_closetime(number(Setup, param::close_time)), m_minvoltage(number(Setup, param::min_voltage)), + m_inittime(number(Setup, param::init_time)), m_initcountdown(m_inittime) + { + } + + void update(frame &Frame) override + { + m_voltage = forward_in(Frame, port::in); + m_current = back_out(Frame, port::out); + // the current of the previous step passes to the supply + add_back(Frame, port::in, m_current); + auto const voltageok{m_voltage > 0.0 && m_voltage >= m_minvoltage}; + // the breaker can be closed after the voltage was present for the init time + m_initcountdown = voltageok ? m_initcountdown - Frame.dt : m_inittime; + if (m_closed) + { + auto const overload{m_tripcurrent > 0.0 && std::abs(m_current) > m_tripcurrent}; + if (overload || false == voltageok || get_bool(Frame, port::open) || false == get_bool(Frame, port::hold)) + { + m_closed = false; + m_tripped = m_tripped || overload; + } + } + else if (get_bool(Frame, port::close) && voltageok && m_initcountdown <= 0.0 && get_bool(Frame, port::hold) && false == get_bool(Frame, port::open)) + { + m_closing += Frame.dt; + if (m_closing >= m_closetime) + { + m_closed = true; + m_tripped = false; + m_closing = 0.0; + } + } + else + { + m_closing = 0.0; + } + // the vehicle can have more breakers, e.g. one for each supply system; its Mains is closed when any of them is + vehicle(Frame).Mains = Frame.body->mains.any(Frame.step, m_closed); + } + void commit(frame &Frame) override + { + set_forward(Frame, port::out, m_closed ? m_voltage : 0.0); + set_bool(Frame, port::closed, m_closed); + set(Frame, port::i, m_current); + set_bool(Frame, port::tripped, m_tripped); + } + std::size_t state_count() const override + { + return 1; + } + char const *state_name(std::size_t) const override + { + return "closed"; + } + double state_value(std::size_t) const override + { + return m_closed ? 1.0 : 0.0; + } + + static block_type describe() + { + auto type{make_type("el.hsb", "Line breaker", + "Main (line) breaker. Closes after close is held for close_time, with enough voltage present for init_time; opens with open, without hold, " + "below min_voltage or above trip_current (then tripped is set until the next closing).", + block_origin::extracted)}; + type.ports = {physical_in("in", domain::high_voltage), + signal_in("close", value_type::boolean), + signal_in("open", value_type::boolean, 0.0, "", true), + signal_in("hold", value_type::boolean, 1.0, "", true), + physical_out("out", domain::high_voltage), + signal_out("closed", value_type::boolean), + signal_out("i", value_type::real, "A"), + signal_out("tripped", value_type::boolean)}; + type.params = {number_param("trip_current", 0.0, "A"), number_param("close_time", 0.2, "s"), number_param("min_voltage", 0.0, "V"), number_param("init_time", 0.0, "s")}; + type.deferred = true; + type.create = make_block; + return type; + } + +private: + double const m_tripcurrent; + double const m_closetime; + double const m_minvoltage; + double const m_inittime; + double m_initcountdown; + double m_closing{0.0}; + double m_voltage{0.0}; + double m_current{0.0}; + bool m_closed{false}; + bool m_tripped{false}; +}; + +class el_contactor final : public block +{ +public: + struct port + { + enum : std::size_t + { + in, + on, + out, + closed + }; + }; + + explicit el_contactor(block_setup const &Setup) : block(Setup), m_delay(number(Setup, 0)) {} + + void update(frame &Frame) override + { + m_voltage = forward_in(Frame, port::in); + add_back(Frame, port::in, back_out(Frame, port::out)); + if (get_bool(Frame, port::on)) + { + m_timer += Frame.dt; + m_closed = m_timer >= m_delay; + } + else + { + m_timer = 0.0; + m_closed = false; + } + } + void commit(frame &Frame) override + { + set_forward(Frame, port::out, m_closed ? m_voltage : 0.0); + set_bool(Frame, port::closed, m_closed); + } + + static block_type describe() + { + auto type{make_type("el.contactor", "Contactor", "Connects the output to the input after on was held for the delay; disconnects at once.", block_origin::primitive)}; + type.ports = {physical_in("in", domain::high_voltage), signal_in("on", value_type::boolean), physical_out("out", domain::high_voltage), + signal_out("closed", value_type::boolean)}; + type.params = {number_param("delay", 0.0, "s")}; + type.deferred = true; + type.create = make_block; + return type; + } + +private: + double const m_delay; + double m_timer{0.0}; + double m_voltage{0.0}; + bool m_closed{false}; +}; + +// static converter or motor generator: auxiliary supply from the high voltage, and optionally the onboard low voltage +// (charger). its state is the ConverterFlag of the body; with more converters the flag is set when any of them runs +class el_converter final : public block +{ +public: + struct port + { + enum : std::size_t + { + wn, + on, + aux, + run, + nn + }; + }; + struct param + { + enum : std::size_t + { + power, + start_time, + min_voltage, + out_voltage, + nn_voltage, + aux_kind + }; + }; + + explicit el_converter(block_setup const &Setup) + : block(Setup), m_idlepower(number(Setup, param::power) * 0.05), m_starttime(number(Setup, param::start_time)), m_minvoltage(number(Setup, param::min_voltage)), + m_outvoltage(number(Setup, param::out_voltage)), m_nnvoltage(std::max(0.0, number(Setup, param::nn_voltage))) + { + } + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const voltage{forward_in(Frame, port::wn)}; + auto const on{get_bool(Frame, port::on)}; + if (on && voltage > 0.0 && voltage >= m_minvoltage) + { + m_timer += Frame.dt; + } + else + { + m_timer = 0.0; + } + auto const run{on && voltage > 0.0 && voltage >= m_minvoltage && m_timer >= m_starttime}; + body.ConverterAllow = Frame.body->converter_allow.any(Frame.step, on); + body.ConverterFlag = Frame.body->converter.any(Frame.step, run); + set_forward(Frame, port::aux, run ? m_outvoltage : 0.0); + set_forward(Frame, port::nn, run ? m_nnvoltage : 0.0); + if (run) + { + // power taken by the auxiliaries and the low voltage loads in the previous step, and the losses + auto const nnpower{m_nnvoltage * std::max(0.0, back_out(Frame, port::nn))}; + add_back(Frame, port::wn, (back_out(Frame, port::aux) + nnpower + m_idlepower) / voltage); + } + set_bool(Frame, port::run, run); + } + + static block_type describe() + { + auto type{make_type("el.converter", "Converter", + "Converter of the auxiliary supply. Runs start_time after it was switched on with enough voltage; draws the power of its loads from the high " + "voltage. nn is the onboard low voltage made by its charger (off when nn_voltage is 0). aux_kind only names the auxiliary circuit.", + block_origin::extracted)}; + type.ports = {physical_in("wn", domain::high_voltage), signal_in("on", value_type::boolean), physical_out("aux", domain::aux_ac), signal_out("run", value_type::boolean), + physical_out("nn", domain::low_voltage)}; + // aux_kind describes the auxiliary circuit for the editor (three phase, single phase or direct current); the simulator ignores it + type.params = {number_param("power", 50000.0, "W"), number_param("start_time", 2.0, "s"), number_param("min_voltage", 0.0, "V"), + number_param("out_voltage", 400.0, "V"), number_param("nn_voltage", 0.0, "V"), choice_param("aux_kind", {"ac3", "ac1", "dc"}, "ac3")}; + type.create = make_block; + return type; + } + +private: + double const m_idlepower; + double const m_starttime; + double const m_minvoltage; + double const m_outvoltage; + double const m_nnvoltage; + double m_timer{0.0}; +}; + +// voltage relay: tells whether the supply is within a range, e.g. to recognize the supply system of a multi-system +// vehicle. the simulator gives the vehicles only the voltage of the catenary, so the systems are told apart by it +class el_voltage_relay final : public block +{ +public: + struct port + { + enum : std::size_t + { + in, + ok, + u + }; + }; + struct param + { + enum : std::size_t + { + min_voltage, + max_voltage, + delay + }; + }; + + explicit el_voltage_relay(block_setup const &Setup) + : block(Setup), m_minvoltage(number(Setup, param::min_voltage)), m_maxvoltage(number(Setup, param::max_voltage)), m_delay(number(Setup, param::delay)) + { + } + + void update(frame &Frame) override + { + // the relay draws no current + auto const voltage{forward_in(Frame, port::in)}; + auto const inrange{voltage > 0.0 && voltage >= m_minvoltage && (m_maxvoltage <= 0.0 || voltage <= m_maxvoltage)}; + m_timer = inrange ? m_timer + Frame.dt : 0.0; + set_bool(Frame, port::ok, inrange && m_timer >= m_delay); + set(Frame, port::u, voltage); + } + + static block_type describe() + { + auto type{make_type("el.voltage_relay", "Voltage relay", + "ok is set when the voltage was within min_voltage and max_voltage (0: no upper limit) for the delay. Draws no current; used e.g. to recognize " + "the supply system.", + block_origin::primitive)}; + type.ports = {physical_in("in", domain::high_voltage), signal_out("ok", value_type::boolean), signal_out("u", value_type::real, "V")}; + type.params = {number_param("min_voltage", 2200.0, "V"), number_param("max_voltage", 4000.0, "V"), number_param("delay", 0.5, "s")}; + type.create = make_block; + return type; + } + +private: + double const m_minvoltage; + double const m_maxvoltage; + double const m_delay; + double m_timer{0.0}; +}; + +// main transformer of an ac vehicle, or of the ac branch of a multi-system one, with the rectifier behind it implied. +// the output voltage is the input voltage times the ratio; the current drawn from the input follows from the power +class el_transformer final : public block +{ +public: + struct port + { + enum : std::size_t + { + in, + out + }; + }; + struct param + { + enum : std::size_t + { + ratio, + eff + }; + }; + + explicit el_transformer(block_setup const &Setup) + : block(Setup), m_ratio(std::max(0.0, number(Setup, param::ratio))), m_efficiency(std::clamp(number(Setup, param::eff), 0.01, 1.0)) + { + } + + void update(frame &Frame) override + { + set_forward(Frame, port::out, forward_in(Frame, port::in) * m_ratio); + add_back(Frame, port::in, back_out(Frame, port::out) * m_ratio / m_efficiency); + } + + static block_type describe() + { + auto type{make_type("el.transformer", "Transformer", + "Output voltage is the input voltage times the ratio (out/in); the input current is the output current times ratio / eff. For ac vehicles " + "and the ac branches of multi-system vehicles, with the rectifier implied.", + block_origin::primitive)}; + type.ports = {physical_in("in", domain::high_voltage), physical_out("out", domain::high_voltage)}; + type.params = {number_param("ratio", 0.12, ""), number_param("eff", 0.97, "")}; + type.create = make_block; + return type; + } + +private: + double const m_ratio; + double const m_efficiency; +}; + +// battery of the low voltage circuits; its switch is the Battery of the body +class el_battery final : public block +{ +public: + struct port + { + enum : std::size_t + { + on, + charge, + nn, + ok, + u + }; + }; + struct param + { + enum : std::size_t + { + voltage, + capacity, + min_voltage + }; + }; + + explicit el_battery(block_setup const &Setup) + : block(Setup), m_charger(is_connected(Setup, port::charge)), m_voltage(number(Setup, param::voltage)), m_capacity(std::max(0.1, number(Setup, param::capacity))), + m_minvoltage(number(Setup, param::min_voltage)), m_charge(m_capacity) + { + } + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const on{get_bool(Frame, port::on)}; + if (on) + { + // current of the loads in the previous step, A + m_charge -= std::max(0.0, back_out(Frame, port::nn)) * Frame.dt / 3600.0; + } + if (m_charger && forward_in(Frame, port::charge) > 0.0) + { + // charged at a tenth of the capacity per hour + m_charge += 0.1 * m_capacity * Frame.dt / 3600.0; + } + m_charge = std::clamp(m_charge, 0.0, m_capacity); + // open circuit voltage drops with the charge + auto const voltage{on ? m_voltage * (0.85 + 0.15 * m_charge / m_capacity) : 0.0}; + body.Battery = on; + body.BatteryVoltage = voltage; + set_forward(Frame, port::nn, voltage); + set_bool(Frame, port::ok, on && voltage >= m_minvoltage); + set(Frame, port::u, voltage); + } + std::size_t state_count() const override + { + return 1; + } + char const *state_name(std::size_t) const override + { + return "charge"; + } + double state_value(std::size_t) const override + { + return m_charge; + } + + static block_type describe() + { + auto type{make_type("el.battery", "Battery", + "Battery of the low voltage circuits. ok tells whether its voltage is above min_voltage; it's charged when the charge input has voltage.", + block_origin::extracted)}; + type.ports = {signal_in("on", value_type::boolean), physical_in("charge", domain::aux_ac, true), physical_out("nn", domain::low_voltage), + signal_out("ok", value_type::boolean), signal_out("u", value_type::real, "V")}; + type.params = {number_param("voltage", 110.0, "V"), number_param("capacity", 100.0, "Ah"), number_param("min_voltage", 80.0, "V")}; + type.single = true; + type.create = make_block; + return type; + } + +private: + bool const m_charger; + double const m_voltage; + double const m_capacity; + double const m_minvoltage; + double m_charge; +}; + +} // namespace + +void register_electric_blocks(catalog &Catalog) +{ + Catalog.add(el_pantograph::describe()); + Catalog.add(el_junction::describe()); + Catalog.add(el_hsb::describe()); + Catalog.add(el_contactor::describe()); + Catalog.add(el_converter::describe()); + Catalog.add(el_battery::describe()); + Catalog.add(el_voltage_relay::describe()); + Catalog.add(el_transformer::describe()); +} + +} // namespace vgraph diff --git a/vehicle/graph/blocks/interfaces.cpp b/vehicle/graph/blocks/interfaces.cpp new file mode 100644 index 00000000..0e2ebc0c --- /dev/null +++ b/vehicle/graph/blocks/interfaces.cpp @@ -0,0 +1,468 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/graph_body.h" +#include "vehicle/graph/graph_instance.h" +#include "McZapkie/MOVER.h" + +#include +#include +#include + +// standard interfaces: between the vehicle and its neighbours (brake pipe, feed pipe, train heating), and between the +// vehicle and the AI. the neighbours may be .fiz vehicles, so these blocks work through the couplers of the vehicle body +namespace vgraph +{ + +namespace +{ + +TMoverParameters &vehicle(frame const &Frame) +{ + return *Frame.body->mover; +} + +block_type make_type(std::string Id, std::string Name, std::string Description) +{ + block_type type; + type.id = std::move(Id); + type.category = "interfaces"; + type.name = std::move(Name); + type.description = std::move(Description); + type.origin = block_origin::interface_profile; + return type; +} + +param_def side_param() +{ + return choice_param("side", {"front", "rear"}, "front"); +} + +// end cock of the brake pipe, or of the feed pipe, at one end of the vehicle +class if_cock final : public block +{ +public: + struct port + { + enum : std::size_t + { + pipe, + cock + }; + }; + struct param + { + enum : std::size_t + { + side, + cock + }; + }; + + if_cock(block_setup const &Setup, bool const Brakepipe) + : block(Setup), m_brakepipe(Brakepipe), m_side(choice(Setup, param::side) == 0 ? end::front : end::rear), m_switched(is_connected(Setup, port::cock)), + m_open(boolean(Setup, param::cock)) + { + } + + void update(frame &Frame) override + { + auto const open{m_switched ? get_bool(Frame, port::cock) : m_open}; + auto &coupler{vehicle(Frame).Couplers[m_side]}; + if (m_brakepipe) + { + coupler.BrakePipeCock = open; + } + else + { + coupler.FeedPipeCock = open; + } + } + + static block_type describe(bool const Brakepipe) + { + auto type{Brakepipe ? make_type("if.pg", "Brake pipe coupling", + "End cock of the brake pipe at one end of the vehicle. Open (the default), the brake pipe exchanges air with the neighbouring vehicle.") + : make_type("if.pz", "Feed pipe coupling", + "End cock (yellow) of the feed pipe at one end of the vehicle. Closed by default; open, the feed pipe exchanges air with the neighbour.")}; + type.ports = {Brakepipe ? physical_in("pg", domain::brake_pipe) : physical_in("pz", domain::feed_pipe), signal_in("cock", value_type::boolean, 0.0, "", true)}; + type.params = {side_param(), boolean_param("cock", Brakepipe)}; + if (Brakepipe) + { + type.create = [](block_setup const &Setup) -> std::unique_ptr { return std::make_unique(Setup, true); }; + } + else + { + type.create = [](block_setup const &Setup) -> std::unique_ptr { return std::make_unique(Setup, false); }; + } + return type; + } + +private: + bool const m_brakepipe; + int const m_side; + bool const m_switched; + bool const m_open; +}; + +// train heating supply through the couplers. the voltage comes from the main circuit of the vehicle (Mains) +class if_heat final : public block +{ +public: + struct port + { + enum : std::size_t + { + wn, + on, + i + }; + }; + + explicit if_heat(block_setup const &Setup) : block(Setup), m_side(choice(Setup, 0) == 0 ? end::front : end::rear) {} + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const voltage{forward_in(Frame, port::wn)}; + auto const on{get_bool(Frame, port::on)}; + body.HeatingAllow = on; + body.Heating = on && voltage > 0.0; + auto const current{std::abs(body.Couplers[m_side].power_high.current)}; + add_back(Frame, port::wn, current); + set(Frame, port::i, current); + } + + static block_type describe() + { + auto type{make_type("if.heat", "Train heating", + "Train heating supply through the coupler at one end: on connects the main circuit (behind the line breaker) to the heating line. i is the " + "current the train draws.")}; + type.ports = {physical_in("wn", domain::high_voltage), signal_in("on", value_type::boolean, 1.0, "", true), signal_out("i", value_type::real, "A")}; + type.params = {side_param()}; + type.create = make_block; + return type; + } + +private: + int const m_side; +}; + +// intents of the AI driver, and the feedback it reads. graph vehicles aren't driven by the AI yet: the outputs stay +// at rest until the AI adapter of the graph vehicles is in place +class if_ai final : public block +{ +public: + struct port + { + enum : std::size_t + { + ready, + i_ratio, + slip, + pg, + zg, + bc, + vmax, + dir, + traction, + ed_brake, + brake, + emergency, + local_brake, + prepare, + shutdown, + doors_left, + doors_right, + lights, + horn, + pantographs, + sand + }; + }; + + explicit if_ai(block_setup const &Setup) : block(Setup) {} + + void update(frame &Frame) override + { + auto const &intents{Frame.body->ai}; + set(Frame, port::dir, intents.dir); + set(Frame, port::traction, intents.traction); + set(Frame, port::ed_brake, intents.ed_brake); + set(Frame, port::brake, intents.brake); + set_bool(Frame, port::emergency, intents.emergency); + set(Frame, port::local_brake, intents.local_brake); + set_bool(Frame, port::prepare, intents.prepare); + set_bool(Frame, port::shutdown, intents.shutdown); + set_bool(Frame, port::doors_left, intents.doors_left); + set_bool(Frame, port::doors_right, intents.doors_right); + set(Frame, port::lights, intents.lights); + set_bool(Frame, port::horn, intents.horn); + set_bool(Frame, port::pantographs, intents.pantographs); + set_bool(Frame, port::sand, intents.sand); + auto &feedback{Frame.body->ai_reply}; + feedback.ready = get_bool(Frame, port::ready); + feedback.i_ratio = get(Frame, port::i_ratio); + feedback.slip = get_bool(Frame, port::slip); + feedback.pg = get(Frame, port::pg); + feedback.zg = get(Frame, port::zg); + feedback.bc = get(Frame, port::bc); + feedback.vmax = get(Frame, port::vmax); + } + + static block_type describe() + { + auto type{make_type("if.ai", "AI interface", + "What the AI driver wants (outputs) and what it needs to know (inputs). The vehicle's graph translates the intents to its controls. Not " + "used by the simulator yet: the AI doesn't drive graph vehicles.")}; + type.ports = {signal_in("ready", value_type::boolean, 0.0, "", true), + signal_in("i_ratio", value_type::real, 0.0, "", true), + signal_in("slip", value_type::boolean, 0.0, "", true), + signal_in("pg", value_type::real, 0.0, "bar", true), + signal_in("zg", value_type::real, 0.0, "bar", true), + signal_in("bc", value_type::real, 0.0, "bar", true), + signal_in("vmax", value_type::real, 0.0, "km/h", true), + signal_out("dir", value_type::real), + signal_out("traction", value_type::real), + signal_out("ed_brake", value_type::real), + signal_out("brake", value_type::real), + signal_out("emergency", value_type::boolean), + signal_out("local_brake", value_type::real), + signal_out("prepare", value_type::boolean), + signal_out("shutdown", value_type::boolean), + signal_out("doors_left", value_type::boolean), + signal_out("doors_right", value_type::boolean), + signal_out("lights", value_type::integer), + signal_out("horn", value_type::boolean), + signal_out("pantographs", value_type::boolean), + signal_out("sand", value_type::boolean)}; + type.single = true; + type.create = make_block; + return type; + } +}; + +// control line of the multiple unit control: a named train wire shared by the coupled graph vehicles. every vehicle +// publishes its contribution in its body state (body_state::lines); the others read what it published last, so the +// values pass between the vehicles with up to one step of delay. the line ends at a vehicle without a graph, at +// a vehicle without the line, or at a coupling without the required flag +class if_line final : public block +{ +public: + struct port + { + enum : std::size_t + { + in, + active, + out, + vehicles, + drivers + }; + }; + struct param + { + enum : std::size_t + { + name, + combine, + reverse, + pair, + coupling + }; + }; + enum class combination + { + max, + min, + sum, + maxabs, + any + }; + + explicit if_line(block_setup const &Setup) + : block(Setup), m_name(text(Setup, param::name)), m_pair(text(Setup, param::pair).empty() ? m_name : text(Setup, param::pair)), + m_combine(static_cast(std::max(0, choice(Setup, param::combine)))), m_reverse(boolean(Setup, param::reverse)), + m_flag(coupling_flag(choice(Setup, param::coupling))), m_drives(is_connected(Setup, port::in)) + { + m_hints.fill(0); + } + + void update(frame &Frame) override + { + auto &lines{Frame.body->lines}; + if (m_entry < 0) + { + // the entry of the line in this vehicle, shared with the other blocks of the same line + m_entry = find(lines, m_name, 0); + if (m_entry < 0) + { + lines.push_back({m_name, 0.0, false}); + m_entry = static_cast(lines.size()) - 1; + } + } + auto &own{lines[m_entry]}; + if (m_drives) + { + own.value = get(Frame, port::in); + own.active = get_bool(Frame, port::active); + } + m_drivers = 0; + m_vehicles = 1; + if (own.active) + { + add(own.value); + } + auto const *body{Frame.body->mover}; + for (int side = 0; side < 2; ++side) + { + walk(*body, side, m_hints.data() + side * max_vehicles); + } + set(Frame, port::out, m_drivers > 0 ? m_result : 0.0); + set(Frame, port::vehicles, m_vehicles); + set(Frame, port::drivers, m_drivers); + } + + static block_type describe() + { + auto type{make_type("if.line", "Train line", + "Named control line of the multiple unit control, shared by the coupled vehicles with the line (the coupling needs the selected flag). out " + "combines the values of the vehicles driving the line (in connected and active set), in the orientation of this vehicle: with reverse the " + "value of a vehicle facing the other way changes sign, and such a vehicle gives its pair line (if set) in place of this one.")}; + type.ports = {signal_in("in", value_type::real, 0.0, "", true), signal_in("active", value_type::boolean, 1.0, "", true), signal_out("out", value_type::real), + signal_out("vehicles", value_type::integer), signal_out("drivers", value_type::integer)}; + type.params = {text_param("name", "linia"), choice_param("combine", {"max", "min", "sum", "maxabs", "or"}, "max"), boolean_param("reverse", false), text_param("pair"), + choice_param("coupling", {"control", "permanent", "coupler", "highvoltage", "heating", "power110v", "power24v", "power3x400v"}, "control")}; + type.create = make_block; + return type; + } + +private: + // longest chain of vehicles followed in each direction, also the guard against coupling loops + static constexpr int max_vehicles{64}; + + static int coupling_flag(int const Choice) + { + static int const flags[]{coupling::control, coupling::permanent, coupling::coupler, coupling::highvoltage, coupling::heating, coupling::power110v, coupling::power24v, + coupling::power3x400v}; + return flags[std::clamp(Choice, 0, static_cast(std::size(flags)) - 1)]; + } + + // index of the line with the specified name, checking the hint first, or -1 + static int find(std::vector const &Lines, std::string const &Name, int const Hint) + { + if (Hint >= 0 && Hint < static_cast(Lines.size()) && Lines[Hint].name == Name) + { + return Hint; + } + for (std::size_t idx = 0; idx < Lines.size(); ++idx) + { + if (Lines[idx].name == Name) + { + return static_cast(idx); + } + } + return -1; + } + + void add(double const Value) + { + if (m_drivers == 0) + { + m_result = (m_combine == combination::any ? (Value != 0.0 ? 1.0 : 0.0) : Value); + } + else + { + switch (m_combine) + { + case combination::max: + m_result = std::max(m_result, Value); + break; + case combination::min: + m_result = std::min(m_result, Value); + break; + case combination::sum: + m_result += Value; + break; + case combination::maxabs: + m_result = (std::abs(Value) > std::abs(m_result) ? Value : m_result); + break; + case combination::any: + m_result = (m_result != 0.0 || Value != 0.0 ? 1.0 : 0.0); + break; + } + } + ++m_drivers; + } + + // follows the coupled vehicles from the specified side of the vehicle. a neighbour coupled by the same side as the one + // leading to it (front to front, rear to rear) faces the other way + void walk(TMoverParameters const &Body, int Side, int *Hints) + { + auto const *current{&Body}; + auto facing{1}; // orientation of the current vehicle relative to this one + for (int count = 0; count < max_vehicles; ++count) + { + auto const &coupler{current->Couplers[Side]}; + if (coupler.Connected == nullptr || (coupler.CouplingFlag & m_flag) == 0 || coupler.Connected == &Body) + { + return; + } + auto const *neighbour{coupler.Connected}; + auto const connectedside{coupler.ConnectedNr}; + facing = (Side != connectedside ? facing : -facing); + if (neighbour->Graph == nullptr) + { + return; + } + auto const &lines{neighbour->Graph->body().lines}; + auto const index{find(lines, facing > 0 ? m_name : m_pair, Hints[count])}; + if (index < 0) + { + return; + } + Hints[count] = index; + ++m_vehicles; + auto const &line{lines[index]}; + if (line.active) + { + add(m_reverse && facing < 0 ? -line.value : line.value); + } + current = neighbour; + Side = 1 - connectedside; + } + } + + std::string const m_name; + std::string const m_pair; + combination const m_combine; + bool const m_reverse; + int const m_flag; + bool const m_drives; + int m_entry{-1}; + std::array m_hints; + double m_result{0.0}; + int m_drivers{0}; + int m_vehicles{1}; +}; + +} // namespace + +void register_interface_blocks(catalog &Catalog) +{ + Catalog.add(if_cock::describe(true)); + Catalog.add(if_cock::describe(false)); + Catalog.add(if_heat::describe()); + Catalog.add(if_ai::describe()); + Catalog.add(if_line::describe()); +} + +} // namespace vgraph diff --git a/vehicle/graph/blocks/lua_block.cpp b/vehicle/graph/blocks/lua_block.cpp new file mode 100644 index 00000000..363091c8 --- /dev/null +++ b/vehicle/graph/blocks/lua_block.cpp @@ -0,0 +1,278 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/graph_format.h" +#include "vehicle/graph/graph_lua.h" + +#include + +#ifdef WITH_LUA +#include +#endif + +// function block defined by a lua script. its ports and numeric parameters come from the script +namespace vgraph +{ + +namespace +{ + +bool resolve_lua_block(node_desc const &Node, std::vector &Ports, std::vector &Params, std::string &Error) +{ + if (false == lua_available()) + { + Error = "lua blocks need a simulator built with lua support (WITH_LUA)"; + return false; + } + if (Node.script.empty()) + { + Error = "lua block without a script"; + return false; + } + lua_block_definition definition; + if (false == parse_lua_block(Node.script, Node.id, definition, Error)) + { + Error = "lua: " + Error; + return false; + } + Ports = std::move(definition.ports); + Params = std::move(definition.params); + return true; +} + +#ifdef WITH_LUA + +class lua_block final : public block +{ +public: + explicit lua_block(block_setup const &Setup) : block(Setup), m_host(Setup.lua), m_nodeid(Setup.node_id) + { + for (std::size_t idx = 0; idx < Setup.ports.size(); ++idx) + { + auto const &port{Setup.ports[idx]}; + (port.direction == port_direction::in ? m_inputs : m_outputs).push_back({idx, port.key, port.type}); + } + if (m_host == nullptr || m_host->state() == nullptr) + { + fail("no lua state"); + return; + } + std::string error; + m_definition = m_host->load_block(Setup.script, Setup.node_id, error); + if (m_definition < 0) + { + fail(error); + return; + } + auto *L{m_host->state()}; + auto const top{lua_gettop(L)}; + lua_rawgeti(L, LUA_REGISTRYINDEX, m_definition); + auto const definition{lua_gettop(L)}; + lua_getfield(L, definition, "rate"); + auto const rate{lua_isnumber(L, -1) ? lua_tonumber(L, -1) : 0.0}; + lua_pop(L, 1); + m_period = (rate > 0.0 ? 1.0 / rate : 0.0); + // self: copy of the state table, and the parameters with values from the graph for the numeric ones + lua_newtable(L); + auto const self{lua_gettop(L)}; + lua_getfield(L, definition, "state"); + if (lua_istable(L, -1)) + { + lua_pushnil(L); + while (lua_next(L, -2) != 0) + { + lua_pushvalue(L, -2); + lua_insert(L, -2); + lua_rawset(L, self); + } + } + lua_pop(L, 1); + lua_getfield(L, definition, "params"); + if (lua_istable(L, -1)) + { + lua_pushnil(L); + while (lua_next(L, -2) != 0) + { + if (lua_type(L, -2) == LUA_TSTRING && lua_type(L, -1) == LUA_TNUMBER) + { + auto const paramindex{find_param(Setup.param_defs, std::string{"lua."} + lua_tostring(L, -2))}; + if (paramindex >= 0) + { + lua_pop(L, 1); + lua_pushnumber(L, Setup.params[paramindex].number); + } + } + lua_pushvalue(L, -2); + lua_insert(L, -2); + lua_rawset(L, self); + } + } + lua_pop(L, 1); + m_self = luaL_ref(L, LUA_REGISTRYINDEX); + lua_newtable(L); + m_in = luaL_ref(L, LUA_REGISTRYINDEX); + lua_newtable(L); + m_out = luaL_ref(L, LUA_REGISTRYINDEX); + lua_settop(L, top); + } + + ~lua_block() override + { + if (m_host != nullptr) + { + m_host->release(m_definition); + m_host->release(m_self); + m_host->release(m_in); + m_host->release(m_out); + } + } + + void update(frame &Frame) override + { + if (m_fault) + { + // outputs keep their last values + return; + } + auto dt{Frame.dt}; + if (m_period > 0.0) + { + m_accumulated += Frame.dt; + if (m_accumulated + 1e-9 < m_period) + { + return; + } + dt = m_accumulated; + m_accumulated = 0.0; + } + auto *L{m_host->state()}; + auto const top{lua_gettop(L)}; + lua_rawgeti(L, LUA_REGISTRYINDEX, m_in); + for (auto const &input : m_inputs) + { + auto const value{get(Frame, input.port)}; + switch (input.type) + { + case value_type::boolean: + lua_pushboolean(L, value > 0.5); + break; + case value_type::integer: + lua_pushnumber(L, std::round(value)); + break; + default: + lua_pushnumber(L, value); + break; + } + lua_setfield(L, -2, input.key.c_str()); + } + lua_pop(L, 1); + lua_rawgeti(L, LUA_REGISTRYINDEX, m_definition); + lua_getfield(L, -1, "update"); + lua_remove(L, -2); + lua_rawgeti(L, LUA_REGISTRYINDEX, m_self); + lua_rawgeti(L, LUA_REGISTRYINDEX, m_in); + lua_rawgeti(L, LUA_REGISTRYINDEX, m_out); + lua_pushnumber(L, dt); + std::string error; + if (false == m_host->call(4, error)) + { + lua_settop(L, top); + fail(error); + return; + } + lua_rawgeti(L, LUA_REGISTRYINDEX, m_out); + for (auto const &output : m_outputs) + { + lua_getfield(L, -1, output.key.c_str()); + auto value{0.0}; + if (lua_isboolean(L, -1)) + { + value = (lua_toboolean(L, -1) ? 1.0 : 0.0); + } + else if (lua_type(L, -1) == LUA_TNUMBER) + { + value = lua_tonumber(L, -1); + if (output.type == value_type::integer) + { + value = std::round(value); + } + } + lua_pop(L, 1); + set(Frame, output.port, value); + } + lua_settop(L, top); + } + + std::size_t state_count() const override + { + return 1; + } + char const *state_name(std::size_t) const override + { + return "fault"; + } + double state_value(std::size_t) const override + { + return m_fault ? 1.0 : 0.0; + } + +private: + struct port_entry + { + std::size_t port; + std::string key; + value_type type; + }; + + void fail(std::string const &Reason) + { + // a failing block stops, and the rest of the vehicle keeps working + m_fault = true; + log_message("lua [" + m_nodeid + "]: block stopped: " + Reason); + } + + lua_host *m_host; + std::string m_nodeid; + std::vector m_inputs; + std::vector m_outputs; + int m_definition{-1}; + int m_self{-1}; + int m_in{-1}; + int m_out{-1}; + double m_period{0.0}; + double m_accumulated{0.0}; + bool m_fault{false}; +}; + +#endif + +} // namespace + +void register_script_blocks(catalog &Catalog) +{ + block_type type; + type.id = "lua.block"; + type.category = "script"; + type.name = "Lua block"; + type.description = "Function block defined by a lua 5.1 script: block \"name\" { inputs, outputs, params, state, rate, update = function(self, i, o, dt) ... end }."; + type.origin = block_origin::script; + type.dynamic_ports = true; + type.resolve = resolve_lua_block; +#ifdef WITH_LUA + type.create = make_block; +#else + // graphs with lua blocks are rejected when loaded, so blocks of this type are never created + type.create = nullptr; +#endif + Catalog.add(std::move(type)); +} + +} // namespace vgraph diff --git a/vehicle/graph/blocks/module.cpp b/vehicle/graph/blocks/module.cpp new file mode 100644 index 00000000..1a366e7f --- /dev/null +++ b/vehicle/graph/blocks/module.cpp @@ -0,0 +1,186 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/graph_format.h" + +// modules: graphs of logic made once and used by many vehicles. a node of type "module" names the file of the module; +// when the vehicle graph is loaded, the node is replaced with the blocks of the module (expand_modules), connected +// through the module's interface blocks, module.input and module.output, one per port of the module. +// the interface blocks work also on their own, so a module file can be tried with -graph-run: an input gives its +// default value (forced values come from the script), an output shows the value it receives +namespace vgraph +{ + +namespace +{ + +std::vector interface_types() +{ + std::vector result{"bool", "int", "float"}; + for (std::size_t idx = 1; idx < static_cast(domain::count_); ++idx) + { + result.emplace_back(info(static_cast(idx)).id); + } + return result; +} + +std::vector interface_params(bool const Input) +{ + std::vector params{choice_param("type", interface_types(), "float")}; + if (Input) + { + params.push_back(number_param("default", 0.0)); + } + params.push_back(text_param("label")); + return params; +} + +// the port of an interface block, of the type chosen by its "type" parameter +bool interface_port(node_desc const &Node, bool const Input, std::vector &Ports, std::vector &Params, std::string &Error) +{ + std::string type{"float"}; + for (auto const ¶m : Node.params) + { + if (param.first == "type") + { + type = param.second.text; + } + } + // the input of the module is an output of the interface block inside it, and the other way round + auto const key{Input ? "out" : "in"}; + if (type == "bool" || type == "int" || type == "float") + { + auto const valuetype{type == "bool" ? value_type::boolean : type == "int" ? value_type::integer : value_type::real}; + Ports = {Input ? signal_out(key, valuetype) : signal_in(key, valuetype, 0.0, "", true)}; + } + else + { + domain dom; + if (false == parse_domain(type, dom) || dom == domain::signal) + { + Error = "unknown type \"" + type + "\" of the module port; expected bool, int, float or a physical domain"; + return false; + } + Ports = {Input ? physical_out(key, dom) : physical_in(key, dom, true)}; + } + Params = interface_params(Input); + return true; +} + +bool resolve_input(node_desc const &Node, std::vector &Ports, std::vector &Params, std::string &Error) +{ + return interface_port(Node, true, Ports, Params, Error); +} + +bool resolve_output(node_desc const &Node, std::vector &Ports, std::vector &Params, std::string &Error) +{ + return interface_port(Node, false, Ports, Params, Error); +} + +bool resolve_module(node_desc const &, std::vector &, std::vector &, std::string &Error) +{ + // expand_modules replaces these nodes when the file is loaded, so one left here means the module couldn't be read + Error = "the module wasn't expanded; graphs with modules have to be loaded from a file, next to the module files"; + return false; +} + +class module_input final : public block +{ +public: + explicit module_input(block_setup const &Setup) : block(Setup), m_signal(Setup.ports[0].dom == domain::signal), m_value(number(Setup, 1)) {} + + void update(frame &Frame) override + { + if (m_signal) + { + set(Frame, 0, m_value); + } + else + { + set_forward(Frame, 0, m_value); + } + } + +private: + bool const m_signal; + double const m_value; +}; + +class module_output final : public block +{ +public: + explicit module_output(block_setup const &Setup) : block(Setup), m_signal(Setup.ports[0].dom == domain::signal), m_connected(is_connected(Setup, 0)) {} + + void update(frame &Frame) override + { + if (false == m_connected) + { + return; + } + m_value = m_signal ? get(Frame, 0) : forward_in(Frame, 0); + } + std::size_t state_count() const override + { + return 1; + } + char const *state_name(std::size_t) const override + { + return "value"; + } + double state_value(std::size_t) const override + { + return m_value; + } + +private: + bool const m_signal; + bool const m_connected; + double m_value{0.0}; +}; + +block_type interface_type(bool const Input) +{ + block_type type; + type.id = Input ? "module.input" : "module.output"; + type.category = "module"; + type.name = Input ? "Module input" : "Module output"; + type.description = Input ? "Input of a module: the value given to the module node in the vehicle graph. On its own it gives the default value." + : "Output of a module: the value the module node gives in the vehicle graph."; + type.origin = block_origin::primitive; + type.dynamic_ports = true; + type.resolve = Input ? resolve_input : resolve_output; + type.params = interface_params(Input); + type.create = Input ? make_block : make_block; + return type; +} + +} // namespace + +void register_module_blocks(catalog &Catalog) +{ + Catalog.add(interface_type(true)); + Catalog.add(interface_type(false)); + + block_type type; + type.id = std::string{module_type}; + type.category = "module"; + type.name = "Module"; + type.description = "Graph of logic from another file (a module), used as one block. Its ports are the module.input and module.output blocks " + "of the module; the file is given relative to the vehicle graph."; + type.origin = block_origin::primitive; + type.dynamic_ports = true; + type.resolve = resolve_module; + type.params = {text_param("file")}; + type.create = nullptr; + Catalog.add(std::move(type)); +} + +} // namespace vgraph diff --git a/vehicle/graph/blocks/pneumatic.cpp b/vehicle/graph/blocks/pneumatic.cpp new file mode 100644 index 00000000..c105d0ed --- /dev/null +++ b/vehicle/graph/blocks/pneumatic.cpp @@ -0,0 +1,801 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/graph_body.h" +#include "McZapkie/MOVER.h" + +#include + +// air supply and brakes. the blocks drive the brake equipment of the vehicle body (TMoverParameters): its brake pipe and +// feed pipe, which exchange air with the neighbouring vehicles, and the distributor and driver's brake valves created +// by CheckLocomotiveParameters from the parameters of these blocks, with the same code as for the .fiz vehicles. +// the sequence of the operations follows TMoverParameters::UpdatePipePressure and UpdateScndPipePressure +namespace vgraph +{ + +namespace +{ + +// cross-section of the feed pipe connection, as in TMoverParameters::UpdateScndPipePressure +double constexpr feed_pipe_section{0.5067}; + +TMoverParameters &vehicle(frame const &Frame) +{ + return *Frame.body->mover; +} + +block_type make_type(std::string Id, std::string Category, std::string Name, std::string Description, block_origin Origin) +{ + block_type type; + type.id = std::move(Id); + type.category = std::move(Category); + type.name = std::move(Name); + type.description = std::move(Description); + type.origin = Origin; + return type; +} + +// compressor of the main reservoir +class pn_compressor final : public block +{ +public: + struct port + { + enum : std::size_t + { + power, + power_hv, + on, + air, + run + }; + }; + struct param + { + enum : std::size_t + { + flow, + max_pressure, + min_voltage, + power + }; + }; + + explicit pn_compressor(block_setup const &Setup) + : block(Setup), m_aux(is_connected(Setup, port::power)), m_hv(is_connected(Setup, port::power_hv)), m_flow(number(Setup, param::flow)), + m_maxpressure(number(Setup, param::max_pressure)), m_minvoltage(number(Setup, param::min_voltage)), m_power(number(Setup, param::power)) + { + } + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + // without supply connections the compressor is assumed to be powered + auto powered{false == m_aux && false == m_hv}; + auto const auxvoltage{m_aux ? forward_in(Frame, port::power) : 0.0}; + auto const hvvoltage{m_hv ? forward_in(Frame, port::power_hv) : 0.0}; + powered = powered || (m_aux && auxvoltage >= m_minvoltage && auxvoltage > 0.0) || (m_hv && hvvoltage >= m_minvoltage && hvvoltage > 0.0); + auto const run{powered && get_bool(Frame, port::on) && body.VeselVolume > 0.0 && m_maxpressure > 0.0}; + body.CompressorAllow = get_bool(Frame, port::on); + body.CompressorFlag = run; + if (run) + { + // TMoverParameters::CompressorCheck, compressor working at steady pace + body.CompressedVolume += m_flow * (2.0 * m_maxpressure - body.Compressor) / m_maxpressure * Frame.dt; + if (m_aux && auxvoltage > 0.0) + { + add_back(Frame, port::power, m_power); + } + else if (m_hv && hvvoltage > 0.0) + { + add_back(Frame, port::power_hv, m_power / hvvoltage); + } + } + set_forward(Frame, port::air, body.Compressor); + set_bool(Frame, port::run, run); + } + + static block_type describe() + { + auto type{make_type("pn.compressor", "pneumatics", "Compressor", + "Compressor of the main reservoir. Without a supply connected it's assumed to be powered; with both connected either one is enough.", + block_origin::extracted)}; + type.ports = {physical_in("power", domain::aux_ac, true), physical_in("power_hv", domain::high_voltage, true), signal_in("on", value_type::boolean), + physical_out("air", domain::air), signal_out("run", value_type::boolean)}; + type.params = {fiz(number_param("flow", 0.05, "m3/s"), "Brake:", "CompressorSpeed"), number_param("max_pressure", 10.0, "bar"), number_param("min_voltage", 0.0, "V"), + number_param("power", 10000.0, "W")}; + type.single = true; + type.create = make_block; + return type; + } + +private: + bool const m_aux; + bool const m_hv; + double const m_flow; + double const m_maxpressure; + double const m_minvoltage; + double const m_power; +}; + +// pressure switch of the compressor: asks for air below the lower pressure, until the upper one is reached +class pn_governor final : public block +{ +public: + struct port + { + enum : std::size_t + { + p, + need + }; + }; + struct param + { + enum : std::size_t + { + min, + max + }; + }; + + explicit pn_governor(block_setup const &Setup) : block(Setup), m_min(number(Setup, param::min)), m_max(number(Setup, param::max)) {} + + void update(frame &Frame) override + { + auto const pressure{get(Frame, port::p)}; + if (pressure < m_min) + { + m_need = true; + } + else if (pressure > m_max) + { + m_need = false; + } + vehicle(Frame).CompressorGovernorLock = false == m_need; + } + void commit(frame &Frame) override + { + set_bool(Frame, port::need, m_need); + } + std::size_t state_count() const override + { + return 1; + } + char const *state_name(std::size_t) const override + { + return "need"; + } + double state_value(std::size_t) const override + { + return m_need ? 1.0 : 0.0; + } + + static block_type describe() + { + auto type{make_type("pn.governor", "pneumatics", "Pressure governor", "Pressure switch of the compressor: on below min, off above max.", block_origin::primitive)}; + type.ports = {signal_in("p", value_type::real, 0.0, "bar"), signal_out("need", value_type::boolean)}; + type.params = {fiz(number_param("min", 7.5, "bar"), "Brake:", "MinCP"), fiz(number_param("max", 8.5, "bar"), "Brake:", "MaxCP")}; + type.deferred = true; + type.create = make_block; + return type; + } + +private: + double const m_min; + double const m_max; + bool m_need{true}; +}; + +// main reservoir of the vehicle, and its connection with the feed pipe +class pn_reservoir final : public block +{ +public: + struct port + { + enum : std::size_t + { + in, + out, + pz, + p + }; + }; + + explicit pn_reservoir(block_setup const &Setup) : block(Setup) {} + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const dt{Frame.dt}; + if (body.VeselVolume > 0.0) + { + // TMoverParameters::CompressorCheck: safety valve and leaks + body.Compressor = body.CompressedVolume / body.VeselVolume; + body.EmergencyValveOpen = body.Compressor > (body.EmergencyValveOpen ? body.EmergencyValveOff : body.EmergencyValveOn); + if (body.EmergencyValveOpen) + { + body.CompressedVolume -= PF(0, body.Compressor, body.EmergencyValveArea) * dt; + } + body.CompressedVolume = std::max(0.0, body.CompressedVolume - dt * body.AirLeakRate * 0.1); + body.Compressor = body.CompressedVolume / body.VeselVolume; + } + else + { + body.Compressor = 0.0; + } + set_forward(Frame, port::out, body.Compressor); + set_forward(Frame, port::pz, body.ScndPipePress); + set(Frame, port::p, body.Compressor); + } + + // the feed pipe, after the other blocks took their air from it (TMoverParameters::UpdateScndPipePressure) + void late_update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const dt{Frame.dt}; + // the end cocks (if.pz) are checked by the exchange + auto const exchange{body.ScndPipeExchange(dt)}; + if (body.VeselVolume > 0.0) + { + auto const flow{PF(body.Compressor, body.ScndPipePress, feed_pipe_section) * dt}; + body.CompressedVolume += flow / 1000.0; + body.Pipe2->Flow(-flow); + } + body.Pipe2->Flow(exchange); + body.Pipe2->Act(); + body.ScndPipePress = body.Pipe2->P(); + if (body.ScndPipePress < -1.0) + { + body.ScndPipePress = -1.0; + body.Pipe2->CreatePress(-1.0); + body.Pipe2->Act(); + } + if (body.CompressedVolume < 0.0) + { + body.CompressedVolume = 0.0; + } + } + + static block_type describe() + { + auto type{make_type("pn.reservoir", "pneumatics", "Main reservoir", + "The main reservoir of the vehicle. It feeds the feed pipe (pz), which the brake valves and the neighbouring vehicles take air from.", + block_origin::simulator)}; + type.ports = {physical_in("in", domain::air, true), physical_out("out", domain::air), physical_out("pz", domain::feed_pipe), signal_out("p", value_type::real, "bar")}; + type.params = {fiz(number_param("volume", 0.0, "m3"), "Brake:", "Vv")}; + type.single = true; + type.late = true; + type.create = make_block; + return type; + } +}; + +// brake pipe of the vehicle +class br_pipe final : public block +{ +public: + struct port + { + enum : std::size_t + { + in, + vent, + dist, + couplers, + p + }; + }; + + explicit br_pipe(block_setup const &Setup) : block(Setup), m_trainlength(std::max(0.0, number(Setup, 0))) {} + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const dt{Frame.dt}; + if (Frame.body->standalone && m_trainlength > 0.0 && false == m_extended) + { + // without the simulator there are no neighbours: the pipe gets the volume of the train instead + auto const pressure{body.Pipe->P()}; + body.Pipe->CreateCap((std::max(body.Dim.L, 14.0) + 0.5 + m_trainlength) * body.Spg); + body.Pipe->CreatePress(pressure); + m_extended = true; + } + // emergency valve: the vent input, and the emergency brakes the simulator applies to every vehicle + body.EmergencyValveFlow = 0.0; + if (get_bool(Frame, port::vent) || body.RadioStopFlag || body.AlarmChainFlag) + { + body.EmergencyValveFlow = PF(0, body.PipePress, 0.15) * dt; + } + body.Pipe->Flow(-body.EmergencyValveFlow); + body.Pipe->Flow(-body.PipePress * 0.001 * dt); + body.Pipe->Act(); + body.PipePress = body.Pipe->P(); + publish(Frame, body.PipePress); + } + + // exchange with the neighbours, after the distributor took its air + void late_update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + // the end cocks (if.pg) are checked by the exchange + body.Pipe->Flow(body.GetDVc(Frame.dt)); + body.Pipe->Act(); + body.PipePress = body.Pipe->P(); + if (body.PipePress < -1.0) + { + body.PipePress = -1.0; + body.Pipe->CreatePress(-1.0); + body.Pipe->Act(); + } + } + + static block_type describe() + { + auto type{make_type("br.pipe", "brakes", "Brake pipe", + "The brake pipe of the vehicle, connected through the couplers with the neighbouring vehicles. vent opens the emergency valve. " + "preview_train_length adds the pipe of a train of that length when the graph runs without the simulator.", + block_origin::native)}; + type.ports = {physical_in("in", domain::brake_pipe, true), signal_in("vent", value_type::boolean, 0.0, "", true), physical_out("dist", domain::brake_pipe), + physical_out("couplers", domain::brake_pipe), signal_out("p", value_type::real, "bar")}; + type.params = {number_param("preview_train_length", 0.0, "m")}; + type.single = true; + type.late = true; + type.create = make_block; + return type; + } + +private: + double const m_trainlength; // without the simulator: length of the brake pipe of the train behind the vehicle + bool m_extended{false}; + + void publish(frame &Frame, double const Pressure) + { + set_forward(Frame, port::dist, Pressure); + set_forward(Frame, port::couplers, Pressure); + set(Frame, port::p, Pressure); + } +}; + +// driver's brake valve +class br_handle final : public block +{ +public: + struct port + { + enum : std::size_t + { + zg, + pos, + emerg, + reductor, + pg, + p_ctl + }; + }; + + explicit br_handle(block_setup const &Setup) : block(Setup), m_reductor(is_connected(Setup, port::reductor)) {} + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const dt{Frame.dt}; + auto position{get(Frame, port::pos)}; + if (get_bool(Frame, port::emerg)) + { + position = body.Handle->GetPos(bh_EB); + } + body.BrakeCtrlPosR = position; + body.BrakeCtrlPos = static_cast(std::floor(position + 0.5)); + // brake pipe lock in the emergency state, per the .fiz parameters (LPOn, LPOff, HandlePipeUnlockPos, EmergencyCutsOffHandle) + body.LockPipe = body.PipePress < (body.LockPipe ? body.LockPipeOff : body.LockPipeOn); + auto const lock{(body.LockPipe && false == body.UnlockPipe && position > body.HandleUnlock) || (body.EmergencyCutsOffHandle && body.EmergencyValveFlow > 0)}; + auto const supply{lock ? body.PipePress + 0.00001 : body.ScndPipePress}; + body.Handle->SetReductor(m_reductor ? get(Frame, port::reductor) : body.BrakeCtrlPos2); + auto const flow{body.Handle->GetPF(position, body.PipePress, supply, dt, body.EqvtPipePress)}; + if (flow < 0) + { + if (body.Compressor > body.ScndPipePress) + { + body.CompressedVolume += flow / 1500.0; + body.Pipe2->Flow(flow / 3.0); + } + else + { + body.Pipe2->Flow(flow); + } + } + body.Pipe->Flow(-flow); + // normalised as in UpdatePipePressure, for the sounds of the valve + body.dpMainValve = flow / (std::max(body.Dim.L, 1.0) * std::max(body.Spg, 0.01) * 20) / (100.0 * dt); + set_forward(Frame, port::pg, body.PipePress); + set(Frame, port::p_ctl, body.Handle->GetCP()); + } + + static block_type describe() + { + auto type{make_type("br.handle", "brakes", "Driver's brake valve", + "Driver's brake valve (TDriverHandle). pos is the position of the handle in the valve's own scale, e.g. -1..6 for FV4a; emerg moves it to the " + "emergency position. It takes air from the feed pipe of the main reservoir.", + block_origin::simulator)}; + type.ports = {physical_in("zg", domain::air), + signal_in("pos", value_type::real), + signal_in("emerg", value_type::boolean, 0.0, "", true), + signal_in("reductor", value_type::real, 0.0, "bar", true), + physical_out("pg", domain::brake_pipe), + signal_out("p_ctl", value_type::real, "bar")}; + type.params = { + fiz(choice_param("type", {"FV4a", "MHZ_EN57", "MHZ_K5P", "MHZ_K8P", "MHZ_6P", "M394", "Knorr", "St113", "FVel6", "FVE408", "D2", "Westinghouse", "test"}, "FV4a"), "Cntrl.", + "BrakeHandle"), + fiz(boolean_param("auto_overload", false), "Cntrl.", "HAO"), + fiz(boolean_param("manual_overload", false), "Cntrl.", "HMO"), + fiz(number_param("gdp1", 0.0), "Cntrl.", "HGDP1"), + fiz(number_param("gdp2", 0.0), "Cntrl.", "HGDP2"), + fiz(number_param("overload_max", 0.0, "bar"), "Cntrl.", "OMP"), + fiz(number_param("overload_decrease", 0.0, "bar/s"), "Cntrl.", "OPD"), + fiz(number_param("lock_on", -1.0, "bar"), "Brake:", "LPOn"), + fiz(number_param("lock_off", -1.0, "bar"), "Brake:", "LPOff"), + fiz(number_param("unlock_pos", -3.0), "Brake:", "HandlePipeUnlockPos"), + fiz(boolean_param("emergency_cuts_off", false), "Brake:", "EmergencyCutsOffHandle"), + }; + type.single = true; + type.create = make_block; + return type; + } + +private: + bool const m_reductor; +}; + +// independent (local) brake valve +class br_local_handle final : public block +{ +public: + struct port + { + enum : std::size_t + { + zg, + pos, + bc + }; + }; + + explicit br_local_handle(block_setup const &Setup) : block(Setup) {} + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const position{std::clamp(get(Frame, port::pos), 0.0, 1.0)}; + body.LocalBrakePosA = position; + body.dpLocalValve = body.LocHandle->GetPF(position, body.Hamulec->GetBCP(), body.ScndPipePress, Frame.dt, 0); + body.LocBrakePress = body.LocHandle->GetCP(); + set_forward(Frame, port::bc, body.LocBrakePress); + } + + static block_type describe() + { + auto type{make_type("br.local_handle", "brakes", "Independent brake valve", + "Independent (local) brake valve of the locomotive. pos 0..1 is the position of the handle; bc is the pressure it sets for the distributor.", + block_origin::simulator)}; + type.ports = {physical_in("zg", domain::air), signal_in("pos", value_type::real), physical_out("bc", domain::air)}; + type.params = {fiz(choice_param("type", {"FD1", "Knorr", "Westinghouse"}, "FD1"), "Cntrl.", "LocBrakeHandle")}; + type.single = true; + type.create = make_block; + return type; + } +}; + +// distributor (brake valve) of the vehicle, with its auxiliary reservoir and brake cylinders +class br_distributor final : public block +{ +public: + struct port + { + enum : std::size_t + { + pg, + feed, + local, + ed, + release, + cyl, + bc, + aux, + crp + }; + }; + + explicit br_distributor(block_setup const &Setup) + : block(Setup), m_local(is_connected(Setup, port::local)), m_ed(is_connected(Setup, port::ed)), m_release(is_connected(Setup, port::release)) + { + } + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const dt{Frame.dt}; + auto &brake{*body.Hamulec}; + brake.ForceLeak(dt * body.AirLeakRate * 0.25); + if (m_local) + { + body.LocBrakePress = forward_in(Frame, port::local); + brake.SetLBP(body.LocBrakePress); + } + // load dependent brake force, as in TMoverParameters::UpdatePipePressure + switch (body.BrakeValve) + { + case TBrakeValve::K: + case TBrakeValve::W: + case TBrakeValve::KE: + case TBrakeValve::EStED: + case TBrakeValve::ESt3AL2: + case TBrakeValve::NESt3: + case TBrakeValve::ESt4: + case TBrakeValve::ESt3: + { + brake.PLC(body.MBPM < 2 ? body.MaxBrakePress[body.LoadFlag] : body.TotalMass - body.Mred); + break; + } + case TBrakeValve::EP2: + case TBrakeValve::EP1: + { + brake.PLC(body.TotalMass - body.Mred); + break; + } + default: + { + break; + } + } + if (m_ed) + { + brake.SetED(std::clamp(get(Frame, port::ed), 0.0, 1.0)); + } + if (m_release) + { + brake.Releaser(get_bool(Frame, port::release) ? 1 : 0); + } + body.CntrlPipePress = brake.GetVRP(); + // the brake pipe + auto const connected{(brake.GetBrakeStatus() & b_dmg) == b_dmg ? 0.0 : 1.0}; + body.Pipe->Flow(connected * brake.GetPF(connected * body.PipePress, dt, body.Vel)); + if (body.ASBType == 128) + { + brake.ASB(int(body.SlippingWheels && body.Vel > 1) * (1 + 2 * int(body.nrot_eps < -0.01))); + } + // the feed pipe + body.Pipe2->Flow(brake.GetHPFlow(body.ScndPipePress, dt)); + + body.BrakePress = brake.GetBCP(); + body.Volume = brake.GetBRP(); + set_forward(Frame, port::cyl, body.BrakePress); + set(Frame, port::bc, body.BrakePress); + set(Frame, port::aux, body.Volume); + set(Frame, port::crp, brake.GetCRP()); + } + + static block_type describe() + { + auto type{make_type("br.distributor", "brakes", "Distributor", + "Distributor of the automatic brake (TBrake), with its auxiliary reservoir and the brake cylinders. local is the pressure of the independent " + "brake valve; ed (0..1) is the share of the electrodynamic brake for the blending of the EStED type; release operates the releaser.", + block_origin::simulator)}; + type.ports = {physical_in("pg", domain::brake_pipe), + physical_in("feed", domain::feed_pipe, true), + physical_in("local", domain::air, true), + signal_in("ed", value_type::real, 0.0, "", true), + signal_in("release", value_type::boolean, 0.0, "", true), + physical_out("cyl", domain::air), + signal_out("bc", value_type::real, "bar"), + signal_out("aux", value_type::real, "bar"), + signal_out("crp", value_type::real, "bar")}; + type.params = { + fiz(choice_param("type", {"W", "W_Lu_L", "W_Lu_XR", "W_Lu_VI", "K", "Kg", "Kp", "Kss", "Kkg", "Kkp", "Kks", "Hikp1", "Hikss", "Hikg1", "KE", "SW", "EStED", "NESt3", "ESt3", + "LSt", "ESt4", "ESt3AL2", "EP1", "EP2", "M483", "CV1_L_TR", "CV1", "CV1_R"}, + "ESt3"), + "Brake:", "BrakeValve"), + fiz(choice_param("mode", {"G", "P", "R", "GP", "PR", "GPR", "GPR+Mg", "PR+Mg"}, "GP"), "Cntrl.", "BrakeDelays"), + fiz(number_param("max_bp", 3.8, "bar"), "Brake:", "MaxBP"), + fiz(number_param("max_lbp", 0.0, "bar"), "Brake:", "MaxLBP"), + fiz(number_param("tare_max_bp", 0.0, "bar"), "Brake:", "TareMaxBP"), + fiz(number_param("max_asbp", 0.0, "bar"), "Brake:", "MaxASBP"), + fiz(number_param("cyl_count", 2.0), "Brake:", "BCN"), + fiz(number_param("cyl_radius", 0.178, "m"), "Brake:", "BCR"), + fiz(number_param("cyl_dist", 0.1, "m"), "Brake:", "BCD"), + fiz(number_param("volume", 0.0, "l"), "Brake:", "BVV"), + fiz(number_param("rm", 1.0), "Brake:", "RM"), + fiz(number_param("rv", 55.0, "km/h"), "Brake:", "RV"), + fiz(number_param("valve_size", 0.0), "Brake:", "Size"), + fiz(number_param("delay1", 0.0, "s"), "Cntrl.", "BDelay1"), + fiz(number_param("delay2", 0.0, "s"), "Cntrl.", "BDelay2"), + fiz(number_param("delay3", 0.0, "s"), "Cntrl.", "BDelay3"), + fiz(number_param("delay4", 0.0, "s"), "Cntrl.", "BDelay4"), + fiz(choice_param("asb", {"none", "manual", "automatic", "yes"}, "none"), "Cntrl.", "ASB"), + }; + type.single = true; + type.create = make_block; + return type; + } + +private: + bool const m_local; + bool const m_ed; + bool const m_release; +}; + +// brake rigging: from the cylinder pressure to the brake force at the wheels +class br_rigging final : public block +{ +public: + struct port + { + enum : std::size_t + { + bc, + spring, + force + }; + }; + struct param + { + enum : std::size_t + { + bcm, + bcs, + bsa, + bre, + nbpa, + material + }; + }; + + explicit br_rigging(block_setup const &Setup) : block(Setup), m_material(make_friction_material(material_id(text(Setup, param::material)))) {} + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + // the cylinders are described by the distributor (BCR, BCN), the rigging by this block; both are in the body + brake_rigging_setup setup; + setup.press_to_force = body.P2FTrans; + setup.cylinder_spring = body.BrakeCylSpring; + setup.cylinder_mult = body.BrakeCylMult[0]; + setup.slack_adjuster = body.BrakeSlckAdj; + setup.rigging_efficiency = body.BrakeRigEff; + setup.cylinder_count = body.BrakeCylNo; + setup.axles = body.NAxles; + setup.blocks_per_axle = body.NBpA; + auto const shoeforce{brake_rigging_shoe_force(setup, std::max(0.0, get(Frame, port::spring)), get(Frame, port::bc), body.Ntotal)}; + auto const friction{m_material->GetFC(shoeforce, body.Vel)}; + body.UnitBrakeForce = friction * shoeforce * 1000.0; + auto const force{body.UnitBrakeForce * body.NAxles * std::max(1, body.NBpA)}; + Frame.body->brake_force += force; + set(Frame, port::force, force); + } + + static block_type describe() + { + auto type{make_type("br.rigging", "brakes", "Brake rigging", + "Brake rigging and shoes or discs: the brake force from the cylinder pressure (bc) and the force of the spring brake (spring, kN). The force is " + "applied at the wheels of the vehicle.", + block_origin::extracted)}; + type.ports = {signal_in("bc", value_type::real, 0.0, "bar"), signal_in("spring", value_type::real, 0.0, "kN", true), signal_out("force", value_type::real, "N")}; + type.params = { + fiz(number_param("bcm", 1.0), "Brake:", "BCM"), + fiz(number_param("bcs", 0.0, "kN"), "Brake:", "BCS"), + fiz(number_param("bsa", 0.0, "kN"), "Brake:", "BSA"), + fiz(number_param("bre", 1.0), "Brake:", "BRE"), + fiz(number_param("nbpa", 1.0), "Brake:", "NBpA"), + fiz(choice_param("material", {"P10", "P10-Bg", "P10-Bgu", "P10yBg", "P10yBgu", "FR513", "FR510", "Cosid", "Disk1", "Disk1+Mg", "Disk2"}, "P10"), "Brake:", "BM"), + }; + type.single = true; + type.create = make_block; + return type; + } + +private: + static int material_id(std::string const &Material) + { + // as in TMoverParameters::LoadFIZ_Brake, without the magnetic track brake + if (Material == "P10-Bg") + return bp_P10Bg; + if (Material == "P10-Bgu") + return bp_P10Bgu; + if (Material == "FR513") + return bp_FR513; + if (Material == "FR510") + return bp_FR510; + if (Material == "Cosid") + return bp_Cosid; + if (Material == "P10yBg") + return bp_P10yBg; + if (Material == "P10yBgu") + return bp_P10yBgu; + if (Material == "Disk1" || Material == "Disk1+Mg") + return bp_D1; + if (Material == "Disk2") + return bp_D2; + return bp_P10; + } + + std::shared_ptr m_material; +}; + +// spring (parking) brake +class pn_spring_brake final : public block +{ +public: + struct port + { + enum : std::size_t + { + release, + feed, + force, + applied + }; + }; + + explicit pn_spring_brake(block_setup const &Setup) : block(Setup) {} + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto &spring{body.SpringBrake}; + spring.Activate = false == get_bool(Frame, port::release); + body.UpdateSpringBrake(Frame.dt); + // the force the spring adds to the shoes, as in TMoverParameters::BrakeForce + auto const force{spring.IsReady ? std::max(0.0, spring.MinForcePressure - spring.Cylinder->P()) * spring.MaxBrakeForce : 0.0}; + set(Frame, port::force, force); + set_bool(Frame, port::applied, spring.IsActive); + } + + static block_type describe() + { + auto type{make_type("pn.spring_brake", "brakes", "Spring brake", + "Spring (parking) brake, released with air from the feed pipe. force (kN) is meant for the spring input of the brake rigging.", + block_origin::extracted)}; + type.ports = {signal_in("release", value_type::boolean), physical_in("feed", domain::air, true), signal_out("force", value_type::real, "kN"), + signal_out("applied", value_type::boolean)}; + type.params = { + fiz(number_param("volume", 1.0, "l"), "SpringBrake:", "Volume"), + fiz(number_param("mbf", 0.0, "kN"), "SpringBrake:", "MBF"), + fiz(number_param("max_sp", 0.0, "bar"), "SpringBrake:", "MaxSP"), + fiz(number_param("reset_p", 0.0, "bar"), "SpringBrake:", "ResetP"), + fiz(number_param("min_fp", 0.1, "bar"), "SpringBrake:", "MinFP"), + fiz(number_param("press_off", -1.0, "bar"), "SpringBrake:", "PressOff"), + fiz(number_param("press_on", -2.0, "bar"), "SpringBrake:", "PressOn"), + fiz(number_param("valve_on_area", 0.0), "SpringBrake:", "ValveOnArea"), + fiz(number_param("valve_off_area", 0.0), "SpringBrake:", "ValveOffArea"), + fiz(number_param("valve_pnb_area", 0.0), "SpringBrake:", "ValvePNBArea"), + }; + type.single = true; + type.create = make_block; + return type; + } +}; + +} // namespace + +void register_pneumatic_blocks(catalog &Catalog) +{ + Catalog.add(pn_compressor::describe()); + Catalog.add(pn_governor::describe()); + Catalog.add(pn_reservoir::describe()); + Catalog.add(br_handle::describe()); + Catalog.add(br_local_handle::describe()); + Catalog.add(br_pipe::describe()); + Catalog.add(br_distributor::describe()); + Catalog.add(br_rigging::describe()); + Catalog.add(pn_spring_brake::describe()); +} + +} // namespace vgraph diff --git a/vehicle/graph/blocks/security.cpp b/vehicle/graph/blocks/security.cpp new file mode 100644 index 00000000..05eb2b40 --- /dev/null +++ b/vehicle/graph/blocks/security.cpp @@ -0,0 +1,149 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/graph_body.h" +#include "McZapkie/MOVER.h" + +// vigilance device and cab signalling (CA/SHP). the block drives the TSecuritySystem of the vehicle body, configured by +// TSecuritySystem::load from the parameters of the block, so the simulator's own uses of it (cab signal magnets of the +// track, the reset commands of the cab, the AI) keep working with graph vehicles +namespace vgraph +{ + +namespace +{ + +class sec_ca final : public block +{ +public: + struct port + { + enum : std::size_t + { + v, + power, + reset, + reset_shp, + active, + lamp, + buzzer, + brake, + shp_lamp + }; + }; + struct param + { + enum : std::size_t + { + aware, + cab_signal, + separate_ack, + sifa, + aware_delay, + sound_delay, + emergency_delay, + min_speed, + max_hold_time, + radio_stop, + cab_dependent + }; + }; + + explicit sec_ca(block_setup const &Setup) : block(Setup), m_separate(boolean(Setup, param::separate_ack)) {} + + void update(frame &Frame) override + { + auto &security{Frame.body->mover->SecuritySystem}; + auto const active{get_bool(Frame, port::active)}; + if (m_first || active != m_active) + { + security.set_enabled(active); + m_active = active; + m_first = false; + } + auto const reset{get_bool(Frame, port::reset)}; + if (reset && false == m_reset) + { + security.acknowledge_press(); + } + else if (false == reset && m_reset) + { + security.acknowledge_release(); + } + m_reset = reset; + auto const resetshp{get_bool(Frame, port::reset_shp)}; + if (resetshp && false == m_resetshp) + { + security.cabsignal_reset(); + } + m_resetshp = resetshp; + + security.update(Frame.dt, get(Frame, port::v), get_bool(Frame, port::power), Frame.body->cab); + + set_bool(Frame, port::lamp, m_separate ? security.is_vigilance_blinking() : security.is_blinking()); + set_bool(Frame, port::buzzer, security.is_beeping() || security.is_cabsignal_beeping()); + set_bool(Frame, port::brake, security.is_braking()); + set_bool(Frame, port::shp_lamp, security.is_cabsignal_blinking()); + } + + static block_type describe() + { + block_type type; + type.id = "sec.ca"; + type.category = "security"; + type.name = "Vigilance device (CA/SHP)"; + type.description = "Vigilance device and cab signalling (TSecuritySystem). v in km/h; brake is meant for the vent input of the brake pipe. The cab signal is " + "set by the magnets of the track, as for the .fiz vehicles."; + type.origin = block_origin::simulator; + type.ports = {signal_in("v", value_type::real, 0.0, "km/h"), + signal_in("power", value_type::boolean), + signal_in("reset", value_type::boolean), + signal_in("reset_shp", value_type::boolean, 0.0, "", true), + signal_in("active", value_type::boolean, 1.0, "", true), + signal_out("lamp", value_type::boolean), + signal_out("buzzer", value_type::boolean), + signal_out("brake", value_type::boolean), + signal_out("shp_lamp", value_type::boolean)}; + // the first four make AwareSystem; a negative min_speed leaves the default of 10% of vmax + type.params = { + boolean_param("aware", true), + boolean_param("cab_signal", false), + boolean_param("separate_ack", false), + boolean_param("sifa", false), + fiz(number_param("aware_delay", 30.0, "s"), "Security:", "AwareDelay"), + fiz(number_param("sound_delay", 5.0, "s"), "Security:", "SoundSignalDelay"), + fiz(number_param("emergency_delay", 5.0, "s"), "Security:", "EmergencyBrakeDelay"), + number_param("min_speed", -1.0, "km/h"), + fiz(number_param("max_hold_time", 1.5, "s"), "Security:", "MaxHoldTime"), + fiz(boolean_param("radio_stop", false), "Security:", "RadioStop"), + fiz(boolean_param("cab_dependent", false), "Security:", "CabDependent"), + }; + type.single = true; + type.create = make_block; + return type; + } + +private: + bool const m_separate; + bool m_first{true}; + bool m_active{false}; + bool m_reset{false}; + bool m_resetshp{false}; +}; + +} // namespace + +void register_security_blocks(catalog &Catalog) +{ + Catalog.add(sec_ca::describe()); +} + +} // namespace vgraph diff --git a/vehicle/graph/blocks/vehicle.cpp b/vehicle/graph/blocks/vehicle.cpp new file mode 100644 index 00000000..d254e4ea --- /dev/null +++ b/vehicle/graph/blocks/vehicle.cpp @@ -0,0 +1,424 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/graph_body.h" +#include "McZapkie/MOVER.h" + +#include +#include + +// the vehicle body: its motion, doors, lights, sander and the animated parts of its model, which stay native in the simulator +namespace vgraph +{ + +namespace +{ + +TMoverParameters &vehicle(frame const &Frame) +{ + return *Frame.body->mover; +} + +block_type make_type(std::string Id, std::string Name, std::string Description) +{ + block_type type; + type.id = std::move(Id); + type.category = "vehicle"; + type.name = std::move(Name); + type.description = std::move(Description); + type.origin = block_origin::native; + return type; +} + +class veh_state final : public block +{ +public: + struct port + { + enum : std::size_t + { + v, + v_signed, + accel, + wheel_rpm, + slipping, + cab, + time + }; + }; + + explicit veh_state(block_setup const &Setup) : block(Setup) {} + + void update(frame &Frame) override + { + auto const &body{*Frame.body}; + set(Frame, port::v, std::abs(body.velocity) * 3.6); + set(Frame, port::v_signed, body.velocity); + set(Frame, port::accel, body.acceleration); + set(Frame, port::wheel_rpm, std::abs(body.wheel_speed) * 60.0); + set_bool(Frame, port::slipping, body.slipping); + set(Frame, port::cab, body.cab); + set(Frame, port::time, Frame.time); + } + + static block_type describe() + { + auto type{make_type("veh.state", "Vehicle state", + "Motion of the vehicle: speed (km/h), signed speed (m/s, positive towards the front, cab 1), acceleration, wheel revolutions, the occupied " + "cab (1, -1, 0) and the time of the graph.")}; + type.ports = {signal_out("v", value_type::real, "km/h"), signal_out("v_signed", value_type::real, "m/s"), signal_out("accel", value_type::real, "m/s2"), + signal_out("wheel_rpm", value_type::real, "1/min"), signal_out("slipping", value_type::boolean), signal_out("cab", value_type::integer), + signal_out("time", value_type::real, "s")}; + type.create = make_block; + return type; + } +}; + +// passenger doors, operated by the driver through the graph; the doors of the vehicle body move them and play their sounds +class veh_doors final : public block +{ +public: + struct port + { + enum : std::size_t + { + open_left, + open_right, + lock, + closed_left, + closed_right, + all_closed + }; + }; + + explicit veh_doors(block_setup const &Setup) : block(Setup), m_lock(is_connected(Setup, port::lock)) {} + + void update(frame &Frame) override + { + auto &doors{vehicle(Frame).Doors}; + operate(doors.instances[side::left], get_bool(Frame, port::open_left)); + operate(doors.instances[side::right], get_bool(Frame, port::open_right)); + if (m_lock) + { + doors.has_lock = true; + doors.lock_enabled = get_bool(Frame, port::lock); + } + // the state from the last update of the doors; they move after the graph + auto const closedleft{doors.instances[side::left].is_closed}; + auto const closedright{doors.instances[side::right].is_closed}; + set_bool(Frame, port::closed_left, closedleft); + set_bool(Frame, port::closed_right, closedright); + set_bool(Frame, port::all_closed, closedleft && closedright); + } + + static block_type describe() + { + auto type{make_type("veh.doors", "Doors", + "Passenger doors of the vehicle. They open while the open input is on and close when it goes off; lock blocks them above the lock speed.")}; + type.ports = {signal_in("open_left", value_type::boolean, 0.0, "", true), + signal_in("open_right", value_type::boolean, 0.0, "", true), + signal_in("lock", value_type::boolean, 0.0, "", true), + signal_out("closed_left", value_type::boolean), + signal_out("closed_right", value_type::boolean), + signal_out("all_closed", value_type::boolean)}; + // open_time and close_time are turned into the speeds of the .fiz with the range + type.params = { + number_param("open_time", 2.0, "s"), + number_param("close_time", 3.0, "s"), + fiz(choice_param("type", {"shift", "rotate", "fold", "plug"}, "rotate"), "Doors:", "DoorOpenMethod"), + fiz(number_param("range", 1.0, "m/deg"), "Doors:", "DoorMaxShiftL"), + fiz(number_param("range_out", 0.0, "m"), "Doors:", "DoorMaxShiftPlug"), + fiz(number_param("open_delay", 0.0, "s"), "Doors:", "DoorOpenDelay"), + fiz(number_param("close_delay", 0.0, "s"), "Doors:", "DoorCloseDelay"), + fiz(number_param("lock_speed", 10.0, "km/h"), "Doors:", "DoorLockSpeed"), + }; + type.single = true; + type.create = make_block; + return type; + } + +private: + template static void operate(Door_ &Door, bool const Open) + { + Door.remote_open = Open; + Door.remote_close = false == Open; + } + + bool const m_lock; +}; + +class veh_lights final : public block +{ +public: + struct port + { + enum : std::size_t + { + front, + rear, + dim + }; + }; + + explicit veh_lights(block_setup const &Setup) : block(Setup) {} + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + body.iLights[end::front] = static_cast(std::lround(get(Frame, port::front))); + body.iLights[end::rear] = static_cast(std::lround(get(Frame, port::rear))); + Frame.body->headlights_dimmed = get_bool(Frame, port::dim); + } + + static block_type describe() + { + auto type{make_type("veh.lights", "Head and tail lights", + "Lights at the front and the rear end of the vehicle, as bit masks of the lights (1 left, 4 upper, 16 right white; 2 left, 32 right red...).")}; + type.ports = {signal_in("front", value_type::integer, 0.0, "", true), signal_in("rear", value_type::integer, 0.0, "", true), signal_in("dim", value_type::boolean, 0.0, "", true)}; + type.single = true; + type.create = make_block; + return type; + } +}; + +class veh_sander final : public block +{ +public: + struct port + { + enum : std::size_t + { + on, + sanding + }; + }; + + explicit veh_sander(block_setup const &Setup) : block(Setup) {} + + void update(frame &Frame) override + { + auto &body{vehicle(Frame)}; + auto const on{get_bool(Frame, port::on)}; + if (on != m_on) + { + body.Sandbox(on, range_t::local); + m_on = on; + } + set_bool(Frame, port::sanding, body.SandDose); + } + + static block_type describe() + { + auto type{make_type("veh.sander", "Sander", "Sands the rails while on, until the sand (body sand_capacity) runs out.")}; + type.ports = {signal_in("on", value_type::boolean), signal_out("sanding", value_type::boolean)}; + type.single = true; + type.create = make_block; + return type; + } + +private: + bool m_on{false}; +}; + +// placement of the animated submodels of the vehicle's external model: the model of the vehicle body or its low poly +// interior. the vehicle (DynObj) reads these parameters by their keys and applies the block's state to the submodels +// right before the vehicle is drawn, so keep the keys and the choice ids +param_def model_param() +{ + return choice_param("model", {"vehicle", "lowpoly"}, "vehicle"); +} + +// submodel of the external model rotated or moved by the value, like the TGauge of a cab control: the input is clamped +// to min..max, followed with the smoothing of TGauge (its friction), and gives offset + scale * value degrees +// (rotation about the submodel's y axis) or millimetres (shift along its z axis) +class veh_anim final : public block +{ +public: + struct port + { + enum : std::size_t + { + value, + anim + }; + }; + struct param + { + enum : std::size_t + { + model, + submodel, + animation, + min, + max, + scale, + offset, + smoothing + }; + }; + + explicit veh_anim(block_setup const &Setup) + : block(Setup), m_min(std::min(number(Setup, param::min), number(Setup, param::max))), m_max(std::max(number(Setup, param::min), number(Setup, param::max))), + m_scale(number(Setup, param::scale)), m_offset(number(Setup, param::offset)), m_smoothing(std::max(0.0, number(Setup, param::smoothing))) + { + } + + void update(frame &Frame) override + { + auto const target{std::clamp(get(Frame, port::value), m_min, m_max)}; + if (m_initialized && m_smoothing > 0.0 && Frame.dt < 0.5 * m_smoothing) + { + // the same as TGauge::Update + m_value += Frame.dt * (target - m_value) / m_smoothing; + if (std::abs(target - m_value) <= 0.0001) + { + m_value = target; + } + } + else + { + // the first step starts at the input, so the part doesn't move when the vehicle appears + m_value = target; + m_initialized = true; + } + set(Frame, port::anim, animation()); + } + std::size_t state_count() const override + { + return 2; + } + char const *state_name(std::size_t Index) const override + { + return Index == 0 ? "value" : "anim"; + } + double state_value(std::size_t Index) const override + { + return Index == 0 ? m_value : animation(); + } + + static block_type describe() + { + auto type{make_type("veh.anim", "Vehicle model animation", + "Rotates or moves a submodel of the vehicle's external model (or its low poly interior) by the value, like a cab control: offset + scale * value, " + "in degrees or millimetres, smoothed. Each vehicle shows its own state, although the model is shared.")}; + type.ports = {signal_in("value", value_type::real), signal_out("anim", value_type::real)}; + type.params = { + model_param(), + text_param("submodel"), + choice_param("animation", {"rotate", "move"}, "rotate"), + number_param("min", 0.0), + number_param("max", 1.0), + number_param("scale", 90.0, "deg/mm"), + number_param("offset", 0.0, "deg/mm"), + number_param("smoothing", 0.5, "s"), + }; + type.create = make_block; + return type; + } + +private: + double animation() const + { + return m_offset + m_scale * m_value; + } + + double const m_min; + double const m_max; + double const m_scale; + double const m_offset; + double const m_smoothing; + double m_value{0.0}; + bool m_initialized{false}; +}; + +// submodel of the external model shown while the input is on, like the TButton of a lamp: submodel visible when on, +// the optional submodel_off visible when off +class veh_visible final : public block +{ +public: + struct port + { + enum : std::size_t + { + on, + shown + }; + }; + struct param + { + enum : std::size_t + { + model, + submodel, + submodel_off, + blink, + blink_period + }; + }; + + explicit veh_visible(block_setup const &Setup) : block(Setup), m_blink(boolean(Setup, param::blink)), m_blinkperiod(std::max(0.01, number(Setup, param::blink_period))) {} + + void update(frame &Frame) override + { + auto on{get_bool(Frame, port::on)}; + if (on && m_blink) + { + // in step with the lamps of the cab, which blink with the time of the graph too + on = std::fmod(Frame.time, m_blinkperiod) < 0.5 * m_blinkperiod; + } + m_shown = on; + set_bool(Frame, port::shown, m_shown); + } + std::size_t state_count() const override + { + return 1; + } + char const *state_name(std::size_t) const override + { + return "on"; + } + double state_value(std::size_t) const override + { + return m_shown ? 1.0 : 0.0; + } + + static block_type describe() + { + auto type{make_type("veh.visible", "Vehicle model element visibility", + "Shows a submodel of the vehicle's external model (or its low poly interior) while the input is on, and the optional off submodel while " + "it's off, e.g. a lamp, a sign or a gadget. Each vehicle shows its own state, although the model is shared.")}; + type.ports = {signal_in("on", value_type::boolean), signal_out("shown", value_type::boolean)}; + type.params = { + model_param(), text_param("submodel"), text_param("submodel_off"), boolean_param("blink", false), number_param("blink_period", 1.0, "s"), + }; + type.create = make_block; + return type; + } + +private: + bool const m_blink; + double const m_blinkperiod; + bool m_shown{false}; +}; + +} // namespace + +void register_vehicle_blocks(catalog &Catalog) +{ + Catalog.add(veh_state::describe()); + Catalog.add(veh_doors::describe()); + Catalog.add(veh_lights::describe()); + Catalog.add(veh_sander::describe()); + Catalog.add(veh_anim::describe()); + Catalog.add(veh_visible::describe()); +} + +} // namespace vgraph diff --git a/vehicle/graph/graph_block.cpp b/vehicle/graph/graph_block.cpp index 5fb48f08..ba722c3c 100644 --- a/vehicle/graph/graph_block.cpp +++ b/vehicle/graph/graph_block.cpp @@ -31,9 +31,14 @@ std::string const &block::text(block_setup const &Setup, std::size_t const Param return Setup.params[Param].text; } +std::vector> const &block::table(block_setup const &Setup, std::size_t const Param) +{ + return Setup.params[Param].rows; +} + int block::choice(block_setup const &Setup, std::size_t const Param) { - auto const &choices{Setup.type.params[Param].choices}; + auto const &choices{Setup.param_defs[Param].choices}; auto const &value{Setup.params[Param].text}; for (std::size_t idx = 0; idx < choices.size(); ++idx) { diff --git a/vehicle/graph/graph_block.h b/vehicle/graph/graph_block.h index c0e85ba8..6478ea1b 100644 --- a/vehicle/graph/graph_block.h +++ b/vehicle/graph/graph_block.h @@ -19,6 +19,8 @@ namespace vgraph { struct block_type; +struct body_state; +class lua_host; // state of a physical connection. the output writes the forward value; every connected input adds its share // of the back value to back_next, which becomes visible to the output in the next step. thanks to this one step lag @@ -35,19 +37,26 @@ struct frame { double dt{0.0}; double time{0.0}; // time elapsed since the start of the instance, after this step + std::uint64_t step{0}; // number of the step, counted from 1; for the blocks combining their effects on the body (shared_value) bool low_voltage{false}; // graph wide switch: simulate the low voltage circuits, or treat them as always powered double *signals{nullptr}; std::uint8_t const *forced{nullptr}; // signal slots with value forced from outside, writes from blocks are ignored link_state *links{nullptr}; + body_state *body{nullptr}; // the vehicle body; never null during update }; // everything a block factory needs to create a block of the given node struct block_setup { block_type const &type; - std::vector const ¶ms; // in the order of the type's parameter definitions - std::vector const &slots; // in the order of the type's port definitions; signal slot or link index - std::vector const &connected; // in the order of the type's port definitions + std::vector const &ports; // ports of the node: the type's, or resolved for the node (lua blocks) + std::vector const ¶m_defs; // parameter definitions of the node, as above + std::vector const ¶ms; // in the order of param_defs + std::vector const &slots; // in the order of ports; signal slot or link index + std::vector const &connected; // in the order of ports + std::string const &node_id; + std::string const &script; // source of a lua block + lua_host *lua{nullptr}; // lua state of the graph instance; null when the graph has no lua blocks }; // base of all blocks. a block reads and writes its ports through slot indices resolved when the graph was compiled @@ -62,6 +71,9 @@ public: // called after all blocks were updated, by the blocks of types marked as deferred. a deferred block publishes its // outputs here, so they become visible to other blocks in the next step; this is what allows feedback loops virtual void commit(frame &) {} + // called after all blocks were updated and before the deferred ones commit, by the blocks of types marked as late. + // used by the blocks which have to see the effects of the whole step, e.g. the brake pipe applying all the flows + virtual void late_update(frame &) {} // internal state shown by the inspector and written by the standalone runner, e.g. whether a lamp is lit virtual std::size_t state_count() const @@ -132,6 +144,7 @@ protected: static double number(block_setup const &Setup, std::size_t Param); static bool boolean(block_setup const &Setup, std::size_t Param); static std::string const &text(block_setup const &Setup, std::size_t Param); + static std::vector> const &table(block_setup const &Setup, std::size_t Param); // index of the parameter's value in the list of choices of its definition static int choice(block_setup const &Setup, std::size_t Param); diff --git a/vehicle/graph/graph_body.cpp b/vehicle/graph/graph_body.cpp new file mode 100644 index 00000000..c7b9f2cc --- /dev/null +++ b/vehicle/graph/graph_body.cpp @@ -0,0 +1,133 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "vehicle/graph/graph_body.h" + +namespace vgraph +{ + +namespace +{ + +body_field number_field(std::string Key, double Default, std::string Unit, std::string Section, std::string Fizkey) +{ + body_field result; + result.key = std::move(Key); + result.type = param_type::number; + result.unit = std::move(Unit); + result.default_value = param_value::make_number(Default); + result.fiz_section = std::move(Section); + result.fiz_key = std::move(Fizkey); + return result; +} + +body_field text_field(std::string Key, std::string Default, std::string Section, std::string Fizkey) +{ + body_field result; + result.key = std::move(Key); + result.type = param_type::text; + result.default_value = param_value::make_text(std::move(Default)); + result.fiz_section = std::move(Section); + result.fiz_key = std::move(Fizkey); + return result; +} + +body_field choice_field(std::string Key, std::vector Choices, std::vector Fizvalues, std::string Default, std::string Section, std::string Fizkey) +{ + body_field result; + result.key = std::move(Key); + result.type = param_type::choice; + result.choices = std::move(Choices); + result.fiz_values = std::move(Fizvalues); + result.default_value = param_value::make_choice(std::move(Default)); + result.fiz_section = std::move(Section); + result.fiz_key = std::move(Fizkey); + return result; +} + +} // namespace + +std::vector const &body_schema() +{ + // the defaults are the values the simulator uses when the entry is missing from the file + static std::vector const schema{ + choice_field("category", {"train", "road", "unimog", "ship"}, {"train", "road", "unimog", "ship"}, "train", "Param.", "Category"), + number_field("mass", 0.0, "kg", "Param.", "M"), + number_field("mass_rotating", 0.0, "kg", "Param.", "Mred"), + number_field("vmax", 0.0, "km/h", "Param.", "Vmax"), + number_field("power", 0.0, "kW", "Param.", "PWR"), + number_field("sand_capacity", 0.0, "kg", "Param.", "SandCap"), + number_field("heating_power", 0.0, "kW", "Param.", "HeatingP"), + number_field("length", 0.0, "m", "Dimensions:", "L"), + number_field("height", 0.0, "m", "Dimensions:", "H"), + number_field("width", 0.0, "m", "Dimensions:", "W"), + number_field("cx", 0.3, "", "Dimensions:", "Cx"), + number_field("wheel_diameter", 0.0, "m", "Wheels:", "D"), + number_field("wheel_inertia", 0.0, "kg m2", "Wheels:", "AIM"), + number_field("track_gauge", 1.435, "m", "Wheels:", "Tw"), + text_field("axle_arrangement", "", "Wheels:", "Axle"), + number_field("axle_distance", 0.0, "m", "Wheels:", "Ad"), + number_field("bogie_distance", 0.0, "m", "Wheels:", "Bd"), + number_field("min_radius", 0.0, "m", "Wheels:", "Rmin"), + choice_field("bearing", {"roll", "slide"}, {"Roll", "Slide"}, "slide", "Wheels:", "BearingType"), + text_field("coupler_type", "", "BuffCoupl.", "CType"), + number_field("coupler_kb", 0.0, "kN/m", "BuffCoupl.", "kB"), + number_field("coupler_dmax_b", 0.1, "m", "BuffCoupl.", "DmaxB"), + number_field("coupler_fmax_b", 1.0, "kN", "BuffCoupl.", "FmaxB"), + number_field("coupler_kc", 0.0, "kN/m", "BuffCoupl.", "kC"), + number_field("coupler_dmax_c", 0.1, "m", "BuffCoupl.", "DmaxC"), + number_field("coupler_fmax_c", 1.0, "kN", "BuffCoupl.", "FmaxC"), + number_field("coupler_beta", 0.0, "", "BuffCoupl.", "beta"), + number_field("coupler_allowed_flag", 3.0, "", "BuffCoupl.", "AllowedFlag"), + text_field("control_type", "", "BuffCoupl.", "ControlType"), + number_field("max_load", 0.0, "", "Load:", "MaxLoad"), + text_field("load_unit", "", "Load:", "LoadQ"), + text_field("load_accepted", "", "Load:", "LoadAccepted"), + number_field("load_speed", 0.0, "1/s", "Load:", "LoadSpeed"), + number_field("unload_speed", 0.0, "1/s", "Load:", "UnLoadSpeed"), + // three numbers, red green blue 0-255; written as LampRed, LampGreen and LampBlue + text_field("light_color", "255 255 255", "Headlights:", "Lamp"), + // current collectors; used when the graph has el.pantograph blocks + text_field("collector_type", "", "Power:", "PantType"), + number_field("collector_min_h", 0.0, "m", "Power:", "MinH"), + number_field("collector_max_h", 0.0, "m", "Power:", "MaxH"), + number_field("collector_csw", 0.0, "m", "Power:", "CSW"), + number_field("collector_max_voltage", 0.0, "V", "Power:", "MaxVoltage"), + number_field("collector_min_voltage", 0.0, "V", "Power:", "MinV"), + number_field("collector_max_current", 0.0, "A", "Power:", "MaxCurrent"), + number_field("collector_layout", 3.0, "", "Power:", "PhysicalLayout"), + }; + return schema; +} + +body_field const *find_body_field(std::string_view const Key) +{ + for (auto const &field : body_schema()) + { + if (field.key == Key) + { + return &field; + } + } + return nullptr; +} + +std::vector const &known_profiles() +{ + static std::vector const profiles{ + {"brake_pipe", 1}, + {"feed_pipe", 1}, + {"heating", 1}, + {"ai", 1}, + }; + return profiles; +} + +} // namespace vgraph diff --git a/vehicle/graph/graph_body.h b/vehicle/graph/graph_body.h new file mode 100644 index 00000000..001ab8ed --- /dev/null +++ b/vehicle/graph/graph_body.h @@ -0,0 +1,144 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include "vehicle/graph/graph_types.h" + +#include +#include +#include +#include +#include + +class TMoverParameters; + +namespace vgraph +{ + +// entry of the "body" section of the .vhg files: parameters of the vehicle body, which stays native in the simulator +// (mass, dimensions, axles, couplers). each entry names the .fiz section and key it replaces; the simulator builds +// the .fiz lines from the entries and reads them with the same code that reads the .fiz files +struct body_field +{ + std::string key; + param_type type{param_type::number}; + std::string unit; + param_value default_value; + std::vector choices; // choice fields only + std::vector fiz_values; // choice fields only: the .fiz value of each choice + std::string fiz_section; // e.g. "Param." + std::string fiz_key; // e.g. "M" +}; + +std::vector const &body_schema(); +body_field const *find_body_field(std::string_view Key); + +// standard interfaces between vehicles, or between the vehicle and the AI, with the versions the simulator knows +struct profile_info +{ + std::string_view id; + int version; +}; + +std::vector const &known_profiles(); + +// what the AI driver wants from the vehicle (if.ai outputs), and what it reads back (if.ai inputs) +struct ai_intents +{ + double dir{0.0}; + double traction{0.0}; // 0..1 + double ed_brake{0.0}; // 0..1 + double brake{0.0}; // train brake demand 0..1, 1 = full service braking + bool emergency{false}; + double local_brake{0.0}; // 0..1 + bool prepare{false}; + bool shutdown{false}; + bool doors_left{false}; + bool doors_right{false}; + int lights{0}; + bool horn{false}; + bool pantographs{false}; + bool sand{false}; +}; + +struct ai_feedback +{ + bool ready{false}; + double i_ratio{0.0}; + bool slip{false}; + double pg{0.0}; + double zg{0.0}; + double bc{0.0}; + double vmax{0.0}; +}; + +// property of the vehicle body set by several blocks, e.g. the Mains closed by any of the line breakers. the first block +// updated in a step starts the value anew and the next ones combine theirs with it, so after the step it covers all of them +struct shared_value +{ + std::uint64_t step{0}; + double value{0.0}; + + // the largest of the values given in the step + double max(std::uint64_t const Step, double const Value) + { + value = (step != Step ? Value : std::max(value, Value)); + step = Step; + return value; + } + // whether any of the values given in the step is set + bool any(std::uint64_t const Step, bool const Value) + { + return max(Step, Value ? 1.0 : 0.0) > 0.5; + } +}; + +// value one vehicle gives to a train line (if.line), read by the vehicles coupled with it +struct train_line_value +{ + std::string name; + double value{0.0}; + bool active{false}; // the vehicle drives the line +}; + +// state of the vehicle body shared with the blocks during a step. the owner of the graph (the simulator, or the +// standalone runner) fills the inputs before every step and reads the outputs after it +struct body_state +{ + // inputs + double velocity{0.0}; // m/s, signed, positive when moving towards the front coupler + double acceleration{0.0}; // m/s^2, signed like the velocity + double wheel_speed{0.0}; // rotational speed of the wheels, rev/s, signed like the velocity + bool slipping{false}; + int cab{0}; // occupied cab: 1, -1 or 0 + double distance{0.0}; // m, travelled + bool standalone{false}; // the graph runs without the simulator (-graph-run): no catenary, no neighbours + // set when the graph has no br.pipe or pn.reservoir: the pipe of the body still passes the air between the neighbours + bool native_brake_pipe{true}; + bool native_feed_pipe{true}; + // outputs, cleared by the owner before every step + double tractive_force{0.0}; // N, at the wheel rim, sum of drive.axle blocks; positive towards the front + double brake_force{0.0}; // N, magnitude, sum of br.rigging blocks + double current{0.0}; // A, drawn from the catenary by the vehicle + bool headlights_dimmed{false}; + // AI interface (if.ai) + ai_intents ai; + ai_feedback ai_reply; + // properties of the body combined from several blocks + shared_value mains; // closed line breakers (el.hsb) + shared_value converter; // running converters (el.converter) + shared_value converter_allow; + // train lines of the vehicle, published by its if.line blocks for the neighbours + std::vector lines; + // body of the vehicle; the standalone runner (-graph-run) creates one as well, so it is never null during update + TMoverParameters *mover{nullptr}; +}; + +} // namespace vgraph diff --git a/vehicle/graph/graph_catalog.cpp b/vehicle/graph/graph_catalog.cpp index 7bf6b406..cd6cf6be 100644 --- a/vehicle/graph/graph_catalog.cpp +++ b/vehicle/graph/graph_catalog.cpp @@ -9,6 +9,8 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/graph_body.h" +#include "input/command.h" #include @@ -66,6 +68,15 @@ catalog const &catalog::get() catalog result; register_logic_blocks(result); register_cab_blocks(result); + register_script_blocks(result); + register_vehicle_blocks(result); + register_pneumatic_blocks(result); + register_security_blocks(result); + register_electric_blocks(result); + register_drive_blocks(result); + register_diesel_blocks(result); + register_interface_blocks(result); + register_module_blocks(result); return result; }()}; return instance; @@ -100,6 +111,15 @@ nlohmann::json to_json(param_value const &Value) case param_type::text: case param_type::choice: return Value.text; + case param_type::table: + { + auto result{nlohmann::json::array()}; + for (auto const &row : Value.rows) + { + result.push_back(row); + } + return result; + } default: return Value.number; } @@ -142,6 +162,16 @@ std::string catalog::export_json() const item["description"] = type.description; item["origin"] = std::string{to_string(type.origin)}; item["deferred"] = type.deferred; + if (type.single) + { + // at most one block of this type in a graph + item["single"] = true; + } + if (type.dynamic_ports) + { + // ports and parameters come from the node, e.g. from the script of a lua block + item["dynamic_ports"] = true; + } auto &ports = (item["ports"] = nlohmann::ordered_json::array()); for (auto const &port : type.ports) { @@ -182,10 +212,93 @@ std::string catalog::export_json() const { paramitem["choices"] = param.choices; } + if (param.type == param_type::table) + { + paramitem["columns"] = param.columns; + } + if (false == param.fiz_key.empty()) + { + paramitem["fiz"] = param.fiz_section + " " + param.fiz_key; + } params.push_back(std::move(paramitem)); } blocks.push_back(std::move(item)); } + + // parameters of the vehicle body, for the form of the editor + auto &body = (root["body"] = nlohmann::ordered_json::array()); + for (auto const &field : body_schema()) + { + nlohmann::ordered_json item; + item["key"] = field.key; + item["type"] = std::string{to_string(field.type)}; + if (false == field.unit.empty()) + { + item["unit"] = field.unit; + } + item["default"] = to_json(field.default_value); + if (field.type == param_type::choice) + { + item["choices"] = field.choices; + } + item["fiz"] = field.fiz_section + " " + field.fiz_key; + body.push_back(std::move(item)); + } + auto &profiles = (root["profiles"] = nlohmann::ordered_json::array()); + for (auto const &profile : known_profiles()) + { + nlohmann::ordered_json item; + item["id"] = std::string{profile.id}; + item["version"] = profile.version; + profiles.push_back(std::move(item)); + } + // commands of the simulator, which the cab switches can be bound to (by name), for the pickers of the editor + auto &commands = (root["commands"] = nlohmann::ordered_json::array()); + { + std::vector> names; + for (auto const &entry : simulation::commandMap) + { + if (entry.second != user_command::none) + { + names.emplace_back(static_cast(entry.second), entry.first); + } + } + // in the order of the command list, which groups the related commands + std::sort(std::begin(names), std::end(names)); + for (auto const &name : names) + { + nlohmann::ordered_json item; + item["name"] = name.second; + if (name.first < simulation::Commands_descriptions.size()) + { + auto const &description{simulation::Commands_descriptions[name.first]}; + switch (description.target) + { + case command_target::userinterface: + item["target"] = "userinterface"; + break; + case command_target::vehicle: + item["target"] = "vehicle"; + break; + case command_target::signal: + item["target"] = "signal"; + break; + case command_target::entity: + item["target"] = "entity"; + break; + case command_target::simulation: + item["target"] = "simulation"; + break; + default: + break; + } + item["mode"] = (description.mode == command_mode::continuous ? "continuous" : "oneoff"); + } + commands.push_back(std::move(item)); + } + } + // live preview server of the simulator, see the README + root["preview"] = "protocol 1"; return root.dump(1, '\t'); } @@ -270,4 +383,27 @@ param_def choice_param(std::string Key, std::vector Choices, std::s return result; } +param_def table_param(std::string Key, std::vector Columns) +{ + param_def result; + result.key = std::move(Key); + result.type = param_type::table; + result.columns = std::move(Columns); + result.default_value = param_value::make_table({}); + return result; +} + +param_def fiz(param_def Definition, std::string Section, std::string Key) +{ + Definition.fiz_section = std::move(Section); + Definition.fiz_key = std::move(Key); + return Definition; +} + +param_def cab_param() +{ + // "0": the engine room (cab0definition: of the .mmd files) + return choice_param("cab", {"any", "1", "2", "0"}, "any"); +} + } // namespace vgraph diff --git a/vehicle/graph/graph_catalog.h b/vehicle/graph/graph_catalog.h index 03e31ee0..e43213ee 100644 --- a/vehicle/graph/graph_catalog.h +++ b/vehicle/graph/graph_catalog.h @@ -18,6 +18,8 @@ http://mozilla.org/MPL/2.0/. namespace vgraph { +struct node_desc; + // where the behaviour of a block comes from; shown by the editor enum class block_origin : std::uint8_t { @@ -42,9 +44,18 @@ struct block_type // outputs of a deferred block are published after the whole step, so connections leaving it don't take part // in ordering the blocks, and loops passing through it are allowed bool deferred{false}; + // late_update() is called for the blocks of this type after all blocks were updated + bool late{false}; + // at most one block of this type in a graph, because it stands for a part of the vehicle the simulator has only one of + bool single{false}; std::vector ports; std::vector params; block_factory create{nullptr}; + // blocks whose ports and parameters depend on the node, e.g. lua blocks described by their script. the resolver + // fills the node's ports and parameters in place of the ones above; returns false with the reason on failure + bool dynamic_ports{false}; + using port_resolver = bool (*)(node_desc const &Node, std::vector &Ports, std::vector &Params, std::string &Error); + port_resolver resolve{nullptr}; int port_index(std::string_view Key) const; int param_index(std::string_view Key) const; @@ -82,9 +93,23 @@ param_def number_param(std::string Key, double Default, std::string Unit = ""); param_def boolean_param(std::string Key, bool Default); param_def text_param(std::string Key, std::string Default = ""); param_def choice_param(std::string Key, std::vector Choices, std::string Default); +param_def table_param(std::string Key, std::vector Columns); +// marks the parameter as standing for the specified .fiz entry, e.g. fiz(number_param("max_bp", 0.0, "bar"), "Brake:", "MaxBP") +param_def fiz(param_def Definition, std::string Section, std::string Key); +// placement of cab controls and indicators: the cab whose model holds the submodel +param_def cab_param(); // registration functions of the block families, called once when the catalog is built void register_logic_blocks(catalog &Catalog); void register_cab_blocks(catalog &Catalog); +void register_script_blocks(catalog &Catalog); +void register_vehicle_blocks(catalog &Catalog); +void register_pneumatic_blocks(catalog &Catalog); +void register_security_blocks(catalog &Catalog); +void register_electric_blocks(catalog &Catalog); +void register_drive_blocks(catalog &Catalog); +void register_diesel_blocks(catalog &Catalog); +void register_interface_blocks(catalog &Catalog); +void register_module_blocks(catalog &Catalog); } // namespace vgraph diff --git a/vehicle/graph/graph_format.cpp b/vehicle/graph/graph_format.cpp index c037413b..4aba42f6 100644 --- a/vehicle/graph/graph_format.cpp +++ b/vehicle/graph/graph_format.cpp @@ -10,8 +10,10 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "vehicle/graph/graph_format.h" +#include #include #include +#include #include #include @@ -57,6 +59,32 @@ bool read_value(json const &Value, param_value &Result) Result = param_value::make_text(Value.get()); return true; } + if (Value.is_array()) + { + // table: array of rows, each row an array of numbers + std::vector> rows; + rows.reserve(Value.size()); + for (auto const &row : Value) + { + if (false == row.is_array()) + { + return false; + } + std::vector cells; + cells.reserve(row.size()); + for (auto const &cell : row) + { + if (false == cell.is_number()) + { + return false; + } + cells.push_back(cell.get()); + } + rows.emplace_back(std::move(cells)); + } + Result = param_value::make_table(std::move(rows)); + return true; + } return false; } @@ -71,7 +99,7 @@ void read_values(json const &Object, std::vector &Values, std::vect } else { - report(Diagnostics, severity::error, Node, What + " \"" + item.key() + "\" has to be a number, a boolean or a text"); + report(Diagnostics, severity::error, Node, What + " \"" + item.key() + "\" has to be a number, a boolean, a text or a table (array of rows of numbers)"); } } } @@ -195,6 +223,7 @@ bool parse_document_(std::string const &Text, document &Document, std::vector &Diagnostics, std::vector &Chain) +{ + auto ok{true}; + std::vector nodes; + std::vector connections{Document.connections}; + for (auto &node : Document.nodes) + { + if (node.type != module_type) + { + nodes.emplace_back(std::move(node)); + continue; + } + std::string file; + for (auto const ¶m : node.params) + { + if (param.first == "file") + { + file = param.second.text; + } + else + { + report(Diagnostics, severity::warning, node.id, "unknown parameter \"" + param.first + "\" of the module was ignored"); + } + } + std::replace(std::begin(file), std::end(file), '\\', '/'); + if (file.empty()) + { + report(Diagnostics, severity::error, node.id, "module without a file"); + ok = false; + continue; + } + auto const path{(file.front() == '/' || (file.size() > 1 && file[1] == ':')) ? file : Directory + file}; + if (std::find(std::begin(Chain), std::end(Chain), path) != std::end(Chain) || Chain.size() >= 16) + { + report(Diagnostics, severity::error, node.id, "module \"" + file + "\" uses itself"); + ok = false; + continue; + } + // problems of the module file are reported under the module node + std::vector problems; + document module; + auto loaded{load_document(path, module, problems)}; + if (loaded) + { + Chain.push_back(path); + loaded = expand_modules_(module, directory_of(path), problems, Chain); + Chain.pop_back(); + } + if (false == module.body.empty() || false == module.profiles.empty()) + { + problems.push_back({severity::warning, "", "the body and the profiles of a module are ignored"}); + } + for (auto const &problem : problems) + { + report(Diagnostics, problem.level, node.id + (problem.node.empty() ? "" : "/" + problem.node), "module \"" + file + "\": " + problem.message); + } + if (false == loaded) + { + ok = false; + continue; + } + + auto const prefix{node.id + "/"}; + std::set inputs, outputs; + std::map inputnodes; + for (auto &inner : module.nodes) + { + // the interface of this module; blocks of the modules it uses have ids with '/', and an input block kept + // by such a module (for its unconnected input) isn't an input of this one + auto const own{inner.id.find('/') == std::string::npos}; + if (own && inner.type == "module.input") + { + inputs.insert(inner.id); + inputnodes.emplace(inner.id, inner); + } + else if (own && inner.type == "module.output") + { + outputs.insert(inner.id); + } + else + { + inner.id = prefix + inner.id; + nodes.emplace_back(std::move(inner)); + } + } + // connections inside the module + std::map> inputtargets; // input of the module -> inputs of its blocks + std::map outputsources; // output of the module -> output of a block, or "input" node for a passed input + for (auto const &inner : module.connections) + { + auto const frominput{inputs.count(inner.from_node) > 0}; + if (outputs.count(inner.to_node) > 0) + { + outputsources[inner.to_node] = frominput ? endpoint{inner.from_node, ""} : endpoint{prefix + inner.from_node, inner.from_port}; + } + else if (frominput) + { + inputtargets[inner.from_node].push_back({prefix + inner.to_node, inner.to_port}); + } + else + { + connections.push_back({prefix + inner.from_node, inner.from_port, prefix + inner.to_node, inner.to_port}); + } + } + // connections of the module node in the graph using it + std::map externalsources; // input of the module -> what feeds it + std::map> externaltargets; // output of the module -> what it feeds + for (auto it = std::begin(connections); it != std::end(connections);) + { + if (it->to_node == node.id) + { + if (inputs.count(it->to_port) == 0) + { + report(Diagnostics, severity::error, node.id, "module \"" + file + "\" has no input \"" + it->to_port + "\""); + ok = false; + } + externalsources[it->to_port] = {it->from_node, it->from_port}; + it = connections.erase(it); + } + else if (it->from_node == node.id) + { + if (outputs.count(it->from_port) == 0) + { + report(Diagnostics, severity::error, node.id, "module \"" + file + "\" has no output \"" + it->from_port + "\""); + ok = false; + } + externaltargets[it->from_port].push_back({it->to_node, it->to_port}); + it = connections.erase(it); + } + else + { + ++it; + } + } + // an input fed from an output of the same module (a loop through the module) is fed by the block inside it + for (auto &source : externalsources) + { + if (source.second.node != node.id) + { + continue; + } + auto const inner{outputsources.find(source.second.port)}; + if (inner != std::end(outputsources) && false == inner->second.port.empty()) + { + source.second = inner->second; + } + } + // an input without a connection keeps its interface block, which gives the default value of the input + auto const source_of = [&](std::string const &Input) -> endpoint { + auto const source{externalsources.find(Input)}; + if (source != std::end(externalsources)) + { + return source->second; + } + auto const id{prefix + Input}; + if (std::none_of(std::begin(nodes), std::end(nodes), [&](node_desc const &Node) { return Node.id == id; })) + { + auto interfacenode{inputnodes[Input]}; + interfacenode.id = id; + nodes.emplace_back(std::move(interfacenode)); + } + return {id, "out"}; + }; + for (auto const &input : inputtargets) + { + auto const source{source_of(input.first)}; + for (auto const &target : input.second) + { + connections.push_back({source.node, source.port, target.node, target.port}); + } + } + for (auto const &output : externaltargets) + { + auto source{outputsources.find(output.first)}; + if (source == std::end(outputsources)) + { + continue; + } + auto from{source->second}; + if (from.port.empty()) + { + // an input passed straight to an output + from = source_of(from.node); + } + for (auto const &target : output.second) + { + connections.push_back({from.node, from.port, target.node, target.port}); + } + } + } + Document.nodes = std::move(nodes); + Document.connections = std::move(connections); + return ok; +} + +} // namespace + +bool expand_modules(document &Document, std::string const &Directory, std::vector &Diagnostics) +{ + std::vector chain; + return expand_modules_(Document, Directory, Diagnostics, chain); +} + bool parse_document(std::string const &Text, document &Document, std::vector &Diagnostics) { try diff --git a/vehicle/graph/graph_format.h b/vehicle/graph/graph_format.h index b781780f..3691ac7f 100644 --- a/vehicle/graph/graph_format.h +++ b/vehicle/graph/graph_format.h @@ -27,6 +27,7 @@ struct node_desc std::string type; std::string title; std::vector params; // as stored in the file; checked against the catalog when the graph is compiled + std::string script; // source of a lua block ("lua" in the file) }; struct connection_desc @@ -50,6 +51,16 @@ struct document std::vector connections; }; +// type of the nodes standing for modules: graphs of logic kept in their own files (by convention .vhgm) and used as +// one block. the "file" parameter names the module file, relative to the file of the graph using it +inline constexpr std::string_view module_type{"module"}; + +// replaces the module nodes of the document with the blocks of their modules, read from their files relative to +// Directory (with a trailing separator, or empty). the blocks get ids "/", and the +// connections of the module node are passed to the blocks connected to its module.input and module.output blocks. +// modules can use other modules. problems are appended to the diagnostics; returns false on errors +bool expand_modules(document &Document, std::string const &Directory, std::vector &Diagnostics); + // parses the content of a .vhg file. problems are appended to the diagnostics; returns false on errors bool parse_document(std::string const &Text, document &Document, std::vector &Diagnostics); // reads and parses the specified .vhg file diff --git a/vehicle/graph/graph_instance.cpp b/vehicle/graph/graph_instance.cpp index 5d70789d..30575f9a 100644 --- a/vehicle/graph/graph_instance.cpp +++ b/vehicle/graph/graph_instance.cpp @@ -18,16 +18,24 @@ graph_instance::graph_instance(std::shared_ptr Template) : m_template(std::move(Template)), m_signals(m_template->signal_defaults()), m_forced(m_signals.size(), 0), m_links(m_template->link_count()) { auto const &nodes{m_template->nodes()}; + if (m_template->uses_lua()) + { + m_lua = std::make_unique(); + } m_blocks.reserve(nodes.size()); for (auto const &node : nodes) { - block_setup const setup{*node.type, node.params, node.slots, node.connected}; + block_setup const setup{*node.type, node.ports, node.param_defs, node.params, node.slots, node.connected, node.id, node.script, m_lua.get()}; m_blocks.emplace_back(node.type->create(setup)); } m_order.reserve(nodes.size()); for (auto const nodeindex : m_template->order()) { m_order.push_back(m_blocks[nodeindex].get()); + if (nodes[nodeindex].type->late) + { + m_late.push_back(m_blocks[nodeindex].get()); + } if (nodes[nodeindex].type->deferred) { m_deferred.push_back(m_blocks[nodeindex].get()); @@ -41,26 +49,40 @@ graph_instance::graph_instance(std::shared_ptr Template) } } +// blocks release their references to the lua state before it's closed +graph_instance::~graph_instance() +{ + m_blocks.clear(); + m_lua.reset(); +} + frame graph_instance::make_frame(double const Deltatime) { frame result; result.dt = Deltatime; result.time = m_time; + result.step = m_step; result.low_voltage = m_template->low_voltage_simulation(); result.signals = m_signals.data(); result.forced = m_forced.data(); result.links = m_links.data(); + result.body = &m_body; return result; } void graph_instance::update(double const Deltatime) { m_time += Deltatime; + ++m_step; auto frame{make_frame(Deltatime)}; for (auto *item : m_order) { item->update(frame); } + for (auto *item : m_late) + { + item->late_update(frame); + } for (auto *item : m_deferred) { item->commit(frame); diff --git a/vehicle/graph/graph_instance.h b/vehicle/graph/graph_instance.h index 18b39445..e4a9bd8a 100644 --- a/vehicle/graph/graph_instance.h +++ b/vehicle/graph/graph_instance.h @@ -10,6 +10,8 @@ http://mozilla.org/MPL/2.0/. #pragma once #include "vehicle/graph/graph_block.h" +#include "vehicle/graph/graph_body.h" +#include "vehicle/graph/graph_lua.h" #include "vehicle/graph/graph_template.h" #include @@ -24,6 +26,7 @@ class graph_instance { public: explicit graph_instance(std::shared_ptr Template); + ~graph_instance(); // advances the graph by the specified time. the order of evaluation is fixed, and there's no other source // of time or randomness, so the same inputs always produce the same results @@ -37,6 +40,15 @@ public: { return m_time; } + // state of the vehicle body shared with the blocks; filled by the owner of the graph before every step + body_state &body() + { + return m_body; + } + body_state const &body() const + { + return m_body; + } // block of the node with the specified index (see graph_template::nodes()) block &node_block(std::size_t Index) @@ -54,6 +66,10 @@ public: { return m_signals[Slot]; } + link_state const &link(int Index) const + { + return m_links[Index]; + } // keeps the slot at the specified value regardless of what its block writes, until released void force(int Slot, double Value); void release(int Slot); @@ -66,13 +82,18 @@ private: frame make_frame(double Deltatime); std::shared_ptr m_template; + // lua state of the vehicle, created when the graph has lua blocks. declared before the blocks, so it outlives them + std::unique_ptr m_lua; std::vector> m_blocks; // in the order of the template's nodes std::vector m_order; // blocks in the order of evaluation + std::vector m_late; // blocks updated again after all the others std::vector m_deferred; // blocks publishing their outputs after the step std::vector m_signals; std::vector m_forced; std::vector m_links; + body_state m_body; double m_time{0.0}; + std::uint64_t m_step{0}; }; } // namespace vgraph diff --git a/vehicle/graph/graph_lua.cpp b/vehicle/graph/graph_lua.cpp new file mode 100644 index 00000000..76a2e868 --- /dev/null +++ b/vehicle/graph/graph_lua.cpp @@ -0,0 +1,592 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "vehicle/graph/graph_lua.h" + +#include +#include + +#ifdef WITH_LUA +#include +#endif + +namespace vgraph +{ + +namespace +{ + +log_sink &sink() +{ + static log_sink instance; + return instance; +} + +} // namespace + +void set_log_sink(log_sink Sink) +{ + sink() = std::move(Sink); +} + +void log_message(std::string const &Message) +{ + if (sink()) + { + sink()(Message); + } +} + +// block_random + +block_random::block_random(std::uint64_t const Seed) : state(Seed != 0 ? Seed : 0x9E3779B97F4A7C15ull) {} + +double block_random::next() +{ + // xorshift64*: small, fast, and easy to reproduce exactly in the editor + state ^= state >> 12; + state ^= state << 25; + state ^= state >> 27; + return static_cast((state * 0x2545F4914F6CDD1Dull) >> 11) * (1.0 / 9007199254740992.0); +} + +std::uint64_t block_random::seed_from(std::string const &Text) +{ + // fnv-1a + std::uint64_t hash{0xcbf29ce484222325ull}; + for (auto const character : Text) + { + hash ^= static_cast(character); + hash *= 0x100000001b3ull; + } + return hash; +} + +#ifdef WITH_LUA + +namespace +{ + +// keys of values kept out of reach of the scripts: light userdata can't be created from lua +char const block_definition_key{0}; +char const block_name_key{0}; + +// instructions between checks of the budget, and the number of checks allowed in a single call +int constexpr hook_interval{1000}; +int constexpr call_budget{2000}; // 2 million instructions; far more than any block needs in one step + +thread_local int budget_remaining{0}; + +void budget_hook(lua_State *L, lua_Debug *) +{ + if (--budget_remaining <= 0) + { + budget_remaining = call_budget; // leave room for the error handling + luaL_error(L, "instruction budget exceeded, endless loop?"); + } +} + +void open_library(lua_State *L, lua_CFunction Open, char const *Name) +{ + lua_pushcfunction(L, Open); + lua_pushstring(L, Name); + lua_call(L, 1, 0); +} + +// copies the fields of the table at Source into a new table pushed on the stack +void push_copy(lua_State *L, int const Source) +{ + auto const source{Source < 0 ? lua_gettop(L) + Source + 1 : Source}; + lua_newtable(L); + lua_pushnil(L); + while (lua_next(L, source) != 0) + { + lua_pushvalue(L, -2); + lua_insert(L, -2); + lua_rawset(L, -4); + } +} + +// math.random and math.randomseed with the generator in the closure's upvalue, the same semantics as in lua 5.1 +int script_random(lua_State *L) +{ + auto *random{static_cast(lua_touserdata(L, lua_upvalueindex(1)))}; + auto const r{random->next()}; + switch (lua_gettop(L)) + { + case 0: + { + lua_pushnumber(L, r); + return 1; + } + case 1: + { + auto const upper{luaL_checknumber(L, 1)}; + luaL_argcheck(L, 1.0 <= upper, 1, "interval is empty"); + lua_pushnumber(L, std::floor(r * std::floor(upper)) + 1.0); + return 1; + } + case 2: + { + auto const lower{std::floor(luaL_checknumber(L, 1))}; + auto const upper{std::floor(luaL_checknumber(L, 2))}; + luaL_argcheck(L, lower <= upper, 2, "interval is empty"); + lua_pushnumber(L, std::floor(r * (upper - lower + 1.0)) + lower); + return 1; + } + default: + { + return luaL_error(L, "wrong number of arguments"); + } + } +} + +int script_randomseed(lua_State *L) +{ + auto *random{static_cast(lua_touserdata(L, lua_upvalueindex(1)))}; + // splitmix64 finalizer spreads small seeds over the whole state + auto x{static_cast(static_cast(std::floor(luaL_checknumber(L, 1))))}; + x += 0x9E3779B97F4A7C15ull; + x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ull; + x = (x ^ (x >> 27)) * 0x94D049BB133111EBull; + x ^= x >> 31; + *random = block_random{x}; + return 0; +} + +// print sends the message to the log, prefixed with the node id; limited, so a script printing in every step can't flood the log +int script_print(lua_State *L) +{ + auto const count{static_cast(lua_tointeger(L, lua_upvalueindex(2)))}; + if (count >= 50) + { + return 0; + } + lua_pushinteger(L, count + 1); + lua_replace(L, lua_upvalueindex(2)); + std::string message{"lua ["}; + message += lua_tostring(L, lua_upvalueindex(1)); + message += "]:"; + auto const arguments{lua_gettop(L)}; + lua_getglobal(L, "tostring"); + for (int idx = 1; idx <= arguments; ++idx) + { + lua_pushvalue(L, -1); + lua_pushvalue(L, idx); + lua_call(L, 1, 1); + auto const *text{lua_tostring(L, -1)}; + message += ' '; + message += (text != nullptr ? text : "?"); + lua_pop(L, 1); + } + lua_pop(L, 1); + if (count + 1 == 50) + { + message += " (further messages of this block are suppressed)"; + } + log_message(message); + return 0; +} + +// block "name" returns a function, which takes the definition table and stores it in the environment +int script_block_definition(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushvalue(L, lua_upvalueindex(1)); // environment + lua_pushlightuserdata(L, const_cast(&block_definition_key)); + lua_pushvalue(L, 1); + lua_rawset(L, -3); + lua_pushlightuserdata(L, const_cast(&block_name_key)); + lua_pushvalue(L, lua_upvalueindex(2)); + lua_rawset(L, -3); + return 0; +} + +int script_block(lua_State *L) +{ + if (lua_type(L, 1) == LUA_TTABLE) + { + // block { ... } without a name + lua_pushstring(L, ""); + lua_insert(L, 1); + } + luaL_checkstring(L, 1); + lua_pushvalue(L, lua_upvalueindex(1)); // environment + lua_pushvalue(L, 1); // name + lua_pushcclosure(L, script_block_definition, 2); + if (lua_gettop(L) >= 3 && lua_type(L, 2) == LUA_TTABLE) + { + // block("name", { ... }) + lua_pushvalue(L, 2); + lua_call(L, 1, 0); + return 0; + } + return 1; +} + +// libraries available to the scripts. everything else (io, os, package, debug, ffi, jit, loading code) stays out +char const *const base_functions[]{"assert", "error", "ipairs", "next", "pairs", "pcall", "select", "tonumber", + "tostring", "type", "unpack", "xpcall", "rawequal", "rawget", "rawset", "setmetatable", + "getmetatable", "_VERSION"}; +char const *const libraries[]{"string", "table", "math", "bit", "coroutine"}; + +lua_State *create_state() +{ + auto *L{luaL_newstate()}; + if (L == nullptr) + { + return nullptr; + } + // interpreter only: instruction count hooks don't run inside compiled traces, and the budget has to stop endless loops + luaJIT_setmode(L, 0, LUAJIT_MODE_ENGINE | LUAJIT_MODE_OFF); + open_library(L, luaopen_base, ""); + open_library(L, luaopen_table, LUA_TABLIBNAME); + open_library(L, luaopen_string, LUA_STRLIBNAME); + open_library(L, luaopen_math, LUA_MATHLIBNAME); + open_library(L, luaopen_bit, LUA_BITLIBNAME); + // string.dump would give access to bytecode + lua_getglobal(L, "string"); + lua_pushnil(L); + lua_setfield(L, -2, "dump"); + lua_pop(L, 1); + // the metatable of strings leads to the shared string library; hiding it keeps the blocks from changing it for others + lua_pushstring(L, ""); + if (lua_getmetatable(L, -1) != 0) + { + lua_pushboolean(L, 0); + lua_setfield(L, -2, "__metatable"); + lua_pop(L, 1); + } + lua_pop(L, 1); + lua_sethook(L, budget_hook, LUA_MASKCOUNT, hook_interval); + return L; +} + +// pushes a new environment for the block with the specified node id +void push_environment(lua_State *L, std::string const &Nodeid) +{ + lua_newtable(L); + auto const environment{lua_gettop(L)}; + for (auto const *name : base_functions) + { + lua_getglobal(L, name); + lua_setfield(L, environment, name); + } + for (auto const *name : libraries) + { + lua_getglobal(L, name); + if (lua_istable(L, -1)) + { + // copies, so changes made by one block don't leak to the others + push_copy(L, -1); + lua_setfield(L, environment, name); + } + lua_pop(L, 1); + } + // random generator of the block, seeded from the node id + lua_getfield(L, environment, "math"); + auto *random{static_cast(lua_newuserdata(L, sizeof(block_random)))}; + *random = block_random{block_random::seed_from(Nodeid)}; + lua_pushvalue(L, -1); + lua_pushcclosure(L, script_random, 1); + lua_setfield(L, -3, "random"); + lua_pushcclosure(L, script_randomseed, 1); + lua_setfield(L, -2, "randomseed"); + lua_pop(L, 1); + // print to the log + lua_pushstring(L, Nodeid.c_str()); + lua_pushinteger(L, 0); + lua_pushcclosure(L, script_print, 2); + lua_setfield(L, environment, "print"); + lua_pushvalue(L, environment); + lua_setfield(L, environment, "_G"); + // block definition + lua_pushvalue(L, environment); + lua_pushcclosure(L, script_block, 1); + lua_setfield(L, environment, "block"); +} + +// runs the chunk on top of the stack within the budget; returns false with the message on failure +bool protected_call(lua_State *L, int const Arguments, std::string &Error) +{ + budget_remaining = call_budget; + if (lua_pcall(L, Arguments, 0, 0) != 0) + { + auto const *message{lua_tostring(L, -1)}; + Error = (message != nullptr ? message : "unknown error"); + lua_pop(L, 1); + return false; + } + return true; +} + +// loads and runs the script in the environment on top of the stack; leaves the definition table on the stack, or returns false +bool run_script(lua_State *L, std::string const &Script, std::string const &Chunkname, std::string &Error) +{ + auto const environment{lua_gettop(L)}; + auto const chunkname{"=" + Chunkname}; + if (luaL_loadbuffer(L, Script.data(), Script.size(), chunkname.c_str()) != 0) + { + Error = lua_tostring(L, -1); + lua_pop(L, 1); + return false; + } + lua_pushvalue(L, environment); + lua_setfenv(L, -2); + if (false == protected_call(L, 0, Error)) + { + return false; + } + lua_pushlightuserdata(L, const_cast(&block_definition_key)); + lua_rawget(L, environment); + if (false == lua_istable(L, -1)) + { + lua_pop(L, 1); + Error = "the script doesn't call block \"name\" { ... }"; + return false; + } + lua_getfield(L, -1, "update"); + auto const hasupdate{lua_isfunction(L, -1)}; + lua_pop(L, 1); + if (false == hasupdate) + { + lua_pop(L, 1); + Error = "the block has no update(self, i, o, dt) function"; + return false; + } + return true; +} + +bool is_identifier(std::string const &Text) +{ + if (Text.empty() || (Text[0] >= '0' && Text[0] <= '9')) + { + return false; + } + return std::all_of(std::begin(Text), std::end(Text), [](char const Char) { return (Char >= 'a' && Char <= 'z') || (Char >= 'A' && Char <= 'Z') || (Char >= '0' && Char <= '9') || Char == '_'; }); +} + +// reads inputs or outputs of the definition table on top of the stack +bool read_ports(lua_State *L, char const *Field, port_direction const Direction, std::vector &Ports, std::string &Error) +{ + lua_getfield(L, -1, Field); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + return true; + } + if (false == lua_istable(L, -1)) + { + lua_pop(L, 1); + Error = std::string{"\""} + Field + "\" has to be a table"; + return false; + } + std::vector ports; + lua_pushnil(L); + while (lua_next(L, -2) != 0) + { + if (lua_type(L, -2) != LUA_TSTRING || lua_type(L, -1) != LUA_TSTRING) + { + lua_pop(L, 3); + Error = std::string{"entries of \""} + Field + "\" have to look like name = \"bool\" | \"int\" | \"float\""; + return false; + } + std::string const key{lua_tostring(L, -2)}; + std::string type{lua_tostring(L, -1)}; + std::transform(std::begin(type), std::end(type), std::begin(type), [](unsigned char const Char) { return static_cast(std::tolower(Char)); }); + lua_pop(L, 1); + if (false == is_identifier(key)) + { + lua_pop(L, 2); + Error = "port name \"" + key + "\" has to be made of letters, digits and '_'"; + return false; + } + port_def port; + port.key = key; + port.direction = Direction; + port.dom = domain::signal; + port.optional = true; + if (type == "bool") + { + port.type = value_type::boolean; + } + else if (type == "int") + { + port.type = value_type::integer; + } + else if (type == "float") + { + port.type = value_type::real; + } + else + { + lua_pop(L, 2); + Error = "port \"" + key + "\" has unknown type \"" + type + "\", expected bool, int or float"; + return false; + } + ports.push_back(std::move(port)); + } + lua_pop(L, 1); + std::sort(std::begin(ports), std::end(ports), [](port_def const &Left, port_def const &Right) { return Left.key < Right.key; }); + Ports.insert(std::end(Ports), std::begin(ports), std::end(ports)); + return true; +} + +} // namespace + +bool lua_available() +{ + return true; +} + +bool parse_lua_block(std::string const &Script, std::string const &Chunkname, lua_block_definition &Definition, std::string &Error) +{ + lua_host host; + auto *L{host.state()}; + if (L == nullptr) + { + Error = "can't create lua state"; + return false; + } + push_environment(L, Chunkname); + if (false == run_script(L, Script, Chunkname, Error)) + { + return false; + } + // definition table on top of the stack, environment below + lua_pushlightuserdata(L, const_cast(&block_name_key)); + lua_rawget(L, -3); + Definition.name = (lua_isstring(L, -1) ? lua_tostring(L, -1) : ""); + lua_pop(L, 1); + if (false == read_ports(L, "inputs", port_direction::in, Definition.ports, Error) || false == read_ports(L, "outputs", port_direction::out, Definition.ports, Error)) + { + return false; + } + for (std::size_t first = 0; first < Definition.ports.size(); ++first) + { + for (auto second = first + 1; second < Definition.ports.size(); ++second) + { + if (Definition.ports[first].key == Definition.ports[second].key) + { + Error = "\"" + Definition.ports[first].key + "\" is both an input and an output"; + return false; + } + } + } + lua_getfield(L, -1, "params"); + if (lua_istable(L, -1)) + { + lua_pushnil(L); + while (lua_next(L, -2) != 0) + { + // numeric parameters can be set per vehicle in the graph; others stay as the script defines them + if (lua_type(L, -2) == LUA_TSTRING && lua_type(L, -1) == LUA_TNUMBER) + { + param_def param; + param.key = std::string{"lua."} + lua_tostring(L, -2); + param.type = param_type::number; + param.default_value = param_value::make_number(lua_tonumber(L, -1)); + Definition.params.push_back(std::move(param)); + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); + std::sort(std::begin(Definition.params), std::end(Definition.params), [](param_def const &Left, param_def const &Right) { return Left.key < Right.key; }); + lua_getfield(L, -1, "rate"); + if (false == lua_isnil(L, -1)) + { + if (lua_type(L, -1) != LUA_TNUMBER || lua_tonumber(L, -1) < 0.0) + { + Error = "\"rate\" has to be a number of updates per second, 0 or more"; + return false; + } + Definition.rate = lua_tonumber(L, -1); + } + lua_pop(L, 1); + return true; +} + +lua_host::lua_host() : m_state(create_state()) {} + +lua_host::~lua_host() +{ + if (m_state != nullptr) + { + lua_close(m_state); + } +} + +int lua_host::load_block(std::string const &Script, std::string const &Nodeid, std::string &Error) +{ + if (m_state == nullptr) + { + Error = "can't create lua state"; + return LUA_NOREF; + } + auto *L{m_state}; + auto const top{lua_gettop(L)}; + push_environment(L, Nodeid); + if (false == run_script(L, Script, Nodeid, Error)) + { + lua_settop(L, top); + return LUA_NOREF; + } + auto const reference{luaL_ref(L, LUA_REGISTRYINDEX)}; + lua_settop(L, top); + return reference; +} + +bool lua_host::call(int const Arguments, std::string &Error) +{ + return protected_call(m_state, Arguments, Error); +} + +void lua_host::release(int const Reference) +{ + if (m_state != nullptr && Reference != LUA_NOREF && Reference != LUA_REFNIL) + { + luaL_unref(m_state, LUA_REGISTRYINDEX, Reference); + } +} + +#else + +bool lua_available() +{ + return false; +} + +bool parse_lua_block(std::string const &, std::string const &, lua_block_definition &, std::string &Error) +{ + Error = "this build of the simulator has no lua support (WITH_LUA)"; + return false; +} + +lua_host::lua_host() = default; +lua_host::~lua_host() = default; + +int lua_host::load_block(std::string const &, std::string const &, std::string &Error) +{ + Error = "this build of the simulator has no lua support (WITH_LUA)"; + return -1; +} + +bool lua_host::call(int, std::string &Error) +{ + Error = "this build of the simulator has no lua support (WITH_LUA)"; + return false; +} + +void lua_host::release(int) {} + +#endif + +} // namespace vgraph diff --git a/vehicle/graph/graph_lua.h b/vehicle/graph/graph_lua.h new file mode 100644 index 00000000..7af3c0ed --- /dev/null +++ b/vehicle/graph/graph_lua.h @@ -0,0 +1,84 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include "vehicle/graph/graph_types.h" + +#include +#include +#include +#include + +struct lua_State; + +// lua function blocks of the vehicle graph. each vehicle gets a lua state of its own, separate from the scenery's +// one, and each block an environment of its own within it. scripts run in lua 5.1 (luajit) with a restricted set of +// libraries: no files, operating system, modules, ffi or bytecode loading. the jit compiler is turned off for these +// states, so the instruction budget which stops endless loops works reliably +namespace vgraph +{ + +// messages of the lua blocks (print, errors). the simulator directs them to its log, the command line tools to the console +using log_sink = std::function; +void set_log_sink(log_sink Sink); +void log_message(std::string const &Message); + +// whether this build of the simulator can run lua blocks (WITH_LUA) +bool lua_available(); + +// interface of a lua block, read from its script: +// block "name" { inputs = { a = "bool" }, outputs = { q = "float" }, params = { k = 1.0 }, state = { ... }, rate = 20, +// update = function(self, i, o, dt) ... end } +struct lua_block_definition +{ + std::string name; + std::vector ports; // inputs then outputs, each sorted by key so the order doesn't depend on the lua hash + std::vector params; // numeric parameters of the script as "lua.", with the script's values as defaults + double rate{0.0}; // updates per second; 0 updates the block in every step +}; + +// runs the script in a temporary state and reads the block interface. returns false with the reason on failure +bool parse_lua_block(std::string const &Script, std::string const &Chunkname, lua_block_definition &Definition, std::string &Error); + +// lua state shared by the lua blocks of one vehicle +class lua_host +{ +public: + lua_host(); + ~lua_host(); + lua_host(lua_host const &) = delete; + lua_host &operator=(lua_host const &) = delete; + + lua_State *state() const + { + return m_state; + } + // runs the script of a block in a new environment and returns the registry reference of the block definition table, + // or a negative value with the reason on failure. the environment's random generator is seeded from the node id + int load_block(std::string const &Script, std::string const &Nodeid, std::string &Error); + // calls the function placed on the stack below its arguments, within the instruction budget. returns false with the reason on failure + bool call(int Arguments, std::string &Error); + void release(int Reference); + +private: + lua_State *m_state{nullptr}; +}; + +// random generator used by math.random of the lua blocks; the external editor uses the same one, so previews match +struct block_random +{ + std::uint64_t state; + + explicit block_random(std::uint64_t Seed); + double next(); // [0, 1) + static std::uint64_t seed_from(std::string const &Text); +}; + +} // namespace vgraph diff --git a/vehicle/graph/graph_preview.cpp b/vehicle/graph/graph_preview.cpp new file mode 100644 index 00000000..af601540 --- /dev/null +++ b/vehicle/graph/graph_preview.cpp @@ -0,0 +1,534 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" + +#ifdef DBG_NEW +#undef new +#endif +#define ASIO_DISABLE_VISIBILITY +#include + +#include "vehicle/graph/graph_preview.h" +#include "vehicle/graph/graph_instance.h" +#include "vehicle/graph/blocks/cab.h" +#include "vehicle/DynObj.h" +#include "vehicle/Train.h" +#include "model/MdlMngr.h" +#include "model/Model3d.h" +#include "simulation/simulation.h" +#include "utilities/parser.h" +#include "utilities/utilities.h" +#include "utilities/Globals.h" +#include "utilities/Logs.h" + +#include +#include +#include +#include + +namespace vgraph +{ + +namespace +{ + +using json = nlohmann::json; + +int constexpr protocol_version{1}; +// pushes of the values per second +double constexpr push_rate{10.0}; + +class preview_client : public std::enable_shared_from_this +{ +public: + explicit preview_client(asio::io_context &Context) : m_socket(Context) {} + + asio::ip::tcp::socket &socket() + { + return m_socket; + } + void start() + { + read(); + } + bool closed() const + { + return m_closed; + } + // lines received since the last call + std::vector take_lines() + { + std::vector lines; + lines.swap(m_inbox); + return lines; + } + void send(json const &Message) + { + if (m_closed) + { + return; + } + m_outbox.push_back(Message.dump() + "\n"); + if (false == m_writing) + { + write(); + } + } + // vehicle the client watches, empty when not attached + std::string attached; + std::chrono::steady_clock::time_point lastpush; + +private: + void read() + { + auto self{shared_from_this()}; + asio::async_read_until(m_socket, m_buffer, '\n', [self](asio::error_code const &Error, std::size_t const Size) { + if (Error) + { + self->m_closed = true; + return; + } + std::string line{asio::buffers_begin(self->m_buffer.data()), asio::buffers_begin(self->m_buffer.data()) + Size}; + self->m_buffer.consume(Size); + while (false == line.empty() && (line.back() == '\n' || line.back() == '\r')) + { + line.pop_back(); + } + if (false == line.empty()) + { + self->m_inbox.emplace_back(std::move(line)); + } + self->read(); + }); + } + void write() + { + if (m_outbox.empty()) + { + m_writing = false; + return; + } + m_writing = true; + auto self{shared_from_this()}; + asio::async_write(m_socket, asio::buffer(m_outbox.front()), [self](asio::error_code const &Error, std::size_t) { + if (Error) + { + self->m_closed = true; + self->m_writing = false; + return; + } + self->m_outbox.pop_front(); + self->write(); + }); + } + + asio::ip::tcp::socket m_socket; + asio::streambuf m_buffer; + std::vector m_inbox; + std::deque m_outbox; + bool m_writing{false}; + bool m_closed{false}; +}; + +TDynamicObject *find_vehicle(std::string const &Name) +{ + auto *vehicle{simulation::Vehicles.find(Name)}; + return (vehicle != nullptr && vehicle->MoverParameters != nullptr && vehicle->MoverParameters->Graph ? vehicle : nullptr); +} + +json error(std::string const &Message) +{ + return {{"op", "error"}, {"message", Message}}; +} + +// names of the submodels of the model, sorted, for the pickers of the editor +json submodel_names(TModel3d *Model) +{ + std::vector names; + // the siblings are walked in a loop, only the children recursively + std::vector pending{Model->GetSMRoot()}; + while (false == pending.empty()) + { + auto *submodel{pending.back()}; + pending.pop_back(); + for (; submodel != nullptr; submodel = submodel->NextGet()) + { + if (false == submodel->pName.empty()) + { + names.push_back(submodel->pName); + } + if (submodel->ChildGet() != nullptr) + { + pending.push_back(submodel->ChildGet()); + } + } + } + std::sort(std::begin(names), std::end(names)); + names.erase(std::unique(std::begin(names), std::end(names)), std::end(names)); + return names; +} + +json model_entry(TModel3d *Model) +{ + if (Model == nullptr) + { + return nullptr; + } + return {{"model", Model->NameGet()}, {"submodels", submodel_names(Model)}}; +} + +class preview_server +{ +public: + explicit preview_server(int const Port) : m_acceptor(m_context) + { + asio::error_code error; + asio::ip::tcp::endpoint const endpoint{asio::ip::make_address("127.0.0.1"), static_cast(Port)}; + m_acceptor.open(endpoint.protocol(), error); + if (false == static_cast(error)) + { + m_acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true), error); + m_acceptor.bind(endpoint, error); + } + if (false == static_cast(error)) + { + m_acceptor.listen(asio::socket_base::max_listen_connections, error); + } + if (error) + { + ErrorLog("Vehicle graph preview: can't listen on 127.0.0.1:" + std::to_string(Port) + ": " + error.message()); + return; + } + WriteLog("Vehicle graph preview: listening on 127.0.0.1:" + std::to_string(Port)); + accept(); + } + + void update() + { + m_context.restart(); + m_context.poll(); + auto const now{std::chrono::steady_clock::now()}; + for (auto &client : m_clients) + { + for (auto const &line : client->take_lines()) + { + handle(*client, line); + } + if (false == client->attached.empty() && now - client->lastpush >= std::chrono::duration(1.0 / push_rate)) + { + client->lastpush = now; + push_values(*client); + } + } + m_clients.erase(std::remove_if(std::begin(m_clients), std::end(m_clients), [](auto const &Client) { return Client->closed(); }), std::end(m_clients)); + // the writes started above + m_context.poll(); + } + +private: + void accept() + { + auto client{std::make_shared(m_context)}; + m_acceptor.async_accept(client->socket(), [this, client](asio::error_code const &Error) { + if (false == static_cast(Error)) + { + client->start(); + m_clients.push_back(client); + } + accept(); + }); + } + + void handle(preview_client &Client, std::string const &Line) + { + auto const message{json::parse(Line, nullptr, false)}; + if (message.is_discarded() || false == message.is_object() || false == message.contains("op") || false == message["op"].is_string()) + { + Client.send(error("malformed message, expected a json object with \"op\"")); + return; + } + auto const op{message["op"].get()}; + try + { + if (op == "hello") + { + Client.send({{"op", "hello"}, {"version", protocol_version}, {"simulator", "MaSzyna " + Global.asVersion}}); + } + else if (op == "list") + { + auto items{json::array()}; + for (auto const *vehicle : simulation::Vehicles.sequence()) + { + if (vehicle == nullptr || vehicle->MoverParameters == nullptr || nullptr == vehicle->MoverParameters->Graph) + { + continue; + } + items.push_back({{"name", vehicle->name()}, {"file", vehicle->MoverParameters->Graph->definition().path()}, {"graph", vehicle->MoverParameters->Graph->definition().name()}}); + } + Client.send({{"op", "vehicles"}, {"items", items}}); + } + else if (op == "attach") + { + auto const name{message.value("vehicle", std::string{})}; + auto *vehicle{find_vehicle(name)}; + if (vehicle == nullptr) + { + Client.send(error("no graph vehicle named \"" + name + "\"")); + return; + } + Client.attached = name; + Client.lastpush = {}; + Client.send({{"op", "attached"}, {"vehicle", name}, {"file", vehicle->MoverParameters->Graph->definition().path()}}); + } + else if (op == "detach") + { + Client.attached.clear(); + } + else if (op == "switch" || op == "force" || op == "unforce" || op == "reload" || op == "submodels") + { + auto *vehicle{find_vehicle(Client.attached)}; + if (vehicle == nullptr) + { + Client.send(error("not attached to a graph vehicle")); + return; + } + if (op == "switch") + { + operate(Client, *vehicle->MoverParameters->Graph, message); + } + else if (op == "reload") + { + reload(Client, *vehicle); + } + else if (op == "submodels") + { + submodels(Client, *vehicle); + } + else + { + force(Client, *vehicle->MoverParameters->Graph, message, op == "force"); + } + } + else + { + Client.send(error("unknown op \"" + op + "\"")); + } + } + catch (json::exception const &Error) + { + Client.send(error(std::string{"malformed message: "} + Error.what())); + } + } + + void operate(preview_client &Client, graph_instance &Graph, json const &Message) + { + auto const node{Message.value("node", std::string{})}; + auto *control{dynamic_cast(Graph.find_block(node))}; + if (control == nullptr) + { + Client.send(error("\"" + node + "\" isn't a cab switch")); + return; + } + auto const action{Message.value("action", std::string{})}; + auto const value{Message.value("value", 0.0)}; + if (action == "press") + control->press(); + else if (action == "release") + control->release(); + else if (action == "toggle") + control->toggle(); + else if (action == "inc") + control->increase(); + else if (action == "dec") + control->decrease(); + else if (action == "set") + control->set_position(static_cast(std::lround(value))); + else if (action == "value") + control->set_value(value); + else + Client.send(error("unknown switch action \"" + action + "\"")); + } + + void force(preview_client &Client, graph_instance &Graph, json const &Message, bool const Force) + { + auto const port{Message.value("port", std::string{})}; + auto const slot{Graph.definition().find_signal(port)}; + if (slot < 0) + { + Client.send(error("unknown signal port \"" + port + "\"")); + return; + } + if (Force) + { + Graph.force(slot, Message.value("value", 0.0)); + } + else + { + Graph.release(slot); + } + } + + void reload(preview_client &Client, TDynamicObject &Vehicle) + { + auto &body{*Vehicle.MoverParameters}; + std::vector diagnostics; + auto const path{body.Graph->definition().path()}; + auto const graph{load_graph(path, diagnostics)}; + if (graph == nullptr) + { + std::string text; + for (auto const &entry : diagnostics) + { + text += (text.empty() ? "" : "\n") + to_string(entry); + } + Client.send(error(text)); + return; + } + // the body keeps its state; the blocks start anew. the old graph is kept alive, as the cab and the sounds + // of the vehicle hold addresses of its blocks until they are created again below + m_retired.push_back(body.Graph); + auto const old{body.Graph->body()}; + body.Graph = std::make_shared(graph); + auto &state{body.Graph->body()}; + state.mover = &body; + state.native_brake_pipe = old.native_brake_pipe; + state.native_feed_pipe = old.native_feed_pipe; + Vehicle.create_graph_sounds(); + if (simulation::Train != nullptr && simulation::Train->Dynamic() == &Vehicle) + { + simulation::Train->InitializeCab(body.CabOccupied, body.TypeName + ".mmd"); + } + WriteLog("Vehicle graph preview: reloaded " + path); + Client.send({{"op", "reloaded"}}); + } + + // submodels of the models of the vehicle: the vehicle, its low poly interior and the cabs of the .mmd file + void submodels(preview_client &Client, TDynamicObject &Vehicle) + { + auto cabs{json::array()}; + // the cab models are loaded the same way as by TTrain::InitializeCab(), so the models switching the cab would load + // are shared with it. in a network game the cab models are separate for each train: only the cabs of the + // player's train can be looked up then + auto const networked{false == Global.network_servers.empty() || Global.network_client.has_value()}; + auto const occupied{simulation::Train != nullptr && simulation::Train->Dynamic() == &Vehicle}; + if (false == networked || occupied) + { + auto const uid{networked ? static_cast(simulation::Train->id()) : 0}; + std::array listed{false, false, false}; + cParser parser(Vehicle.MoverParameters->TypeName + ".mmd", cParser::buffer_FILE, Vehicle.asBaseDir, true, std::vector(), true); + std::string token; + do + { + token = ""; + parser.getTokens(); + parser >> token; + if (token.size() != 10 || token.compare(0, 3, "cab") != 0 || token.compare(4, 6, "model:") != 0 || token[3] < '0' || token[3] > '2') + { + continue; + } + auto const cabindex{token[3] - '0'}; + token = ""; + parser.getTokens(); + parser >> token; + if (token.empty() || listed[cabindex]) + { + continue; + } + listed[cabindex] = true; + TModel3d *model{nullptr}; + std::replace(token.begin(), token.end(), '\\', '/'); + if (token != "none") + { + erase_leading_slashes(token); + auto const texturepath{Global.asCurrentTexturePath}; + Global.asCurrentTexturePath = Vehicle.asBaseDir; + model = TModelsManager::GetModel(Vehicle.asBaseDir + token, true, false, uid); + Global.asCurrentTexturePath = texturepath; + } + else if (cabindex == 1) + { + // the controls of cab 1 are looked up in the model of the vehicle then + model = Vehicle.mdModel; + } + if (model != nullptr) + { + auto entry{model_entry(model)}; + entry["cab"] = cabindex; + cabs.push_back(std::move(entry)); + } + } while (false == token.empty()); + } + Client.send({{"op", "submodels"}, {"vehicle", model_entry(Vehicle.mdModel)}, {"lowpoly", model_entry(Vehicle.mdLowPolyInt)}, {"cabs", cabs}}); + } + + void push_values(preview_client &Client) + { + auto *vehicle{find_vehicle(Client.attached)}; + if (vehicle == nullptr) + { + Client.send(error("the vehicle \"" + Client.attached + "\" is gone")); + Client.attached.clear(); + return; + } + auto const &graph{*vehicle->MoverParameters->Graph}; + auto const &definition{graph.definition()}; + json signals = json::object(); + json states = json::object(); + json links = json::object(); + auto const &nodes{definition.nodes()}; + for (std::size_t nodeindex = 0; nodeindex < nodes.size(); ++nodeindex) + { + auto const &node{nodes[nodeindex]}; + for (std::size_t portindex = 0; portindex < node.ports.size(); ++portindex) + { + auto const &port{node.ports[portindex]}; + if (port.direction != port_direction::out) + { + continue; + } + auto const name{node.id + "." + port.key}; + if (port.dom == domain::signal) + { + signals[name] = graph.signal(node.slots[portindex]); + } + else + { + auto const &link{graph.link(node.slots[portindex])}; + links[name] = {link.forward, link.back}; + } + } + auto const &block{graph.node_block(nodeindex)}; + for (std::size_t stateindex = 0; stateindex < block.state_count(); ++stateindex) + { + states[node.id + ":" + block.state_name(stateindex)] = block.state_value(stateindex); + } + } + Client.send({{"op", "values"}, {"time", graph.time()}, {"signals", signals}, {"states", states}, {"links", links}}); + } + + asio::io_context m_context; + asio::ip::tcp::acceptor m_acceptor; + std::vector> m_clients; + std::vector> m_retired; +}; + +} // namespace + +void preview_update() +{ + if (false == Global.vehiclegraph_preview) + { + return; + } + static preview_server server{Global.vehiclegraph_preview_port}; + server.update(); +} + +} // namespace vgraph diff --git a/vehicle/graph/graph_preview.h b/vehicle/graph/graph_preview.h new file mode 100644 index 00000000..a011ff6d --- /dev/null +++ b/vehicle/graph/graph_preview.h @@ -0,0 +1,22 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +// live preview of the vehicle graphs for the external editor: a TCP server on the local machine, speaking json lines +// (protocol 1, see tools/vehicle_graph/README.md). off unless enabled with -graph-preview [port] or with +// "vehiclegraph_preview yes [port]" in eu07.ini. everything runs on the simulation thread: the connections are served +// by polling, once per frame +namespace vgraph +{ + +// serves the preview connections; does nothing when the preview is disabled +void preview_update(); + +} // namespace vgraph diff --git a/vehicle/graph/graph_standalone.cpp b/vehicle/graph/graph_standalone.cpp index 063424e1..6cf9d0b0 100644 --- a/vehicle/graph/graph_standalone.cpp +++ b/vehicle/graph/graph_standalone.cpp @@ -11,11 +11,15 @@ http://mozilla.org/MPL/2.0/. #include "vehicle/graph/graph_standalone.h" #include "vehicle/graph/graph_catalog.h" #include "vehicle/graph/graph_instance.h" +#include "vehicle/graph/graph_lua.h" #include "vehicle/graph/blocks/cab.h" +#include "McZapkie/MOVER.h" #include #include +#include #include +#include #include // command line tools of the vehicle graph. they run without the rest of the simulator; the log service doesn't run @@ -33,6 +37,7 @@ struct script_event std::string target; double value{0.0}; int line{0}; + std::size_t vehicle{0}; // index of the vehicle in the consist; the target was prefixed "k:" for the vehicle k }; bool load_script(std::string const &Path, std::vector &Events) @@ -140,6 +145,42 @@ bool apply_event(graph_instance &Instance, script_event const &Event) return true; } +// motion of the vehicle without the track: the forces of the graph, the motion resistance and the mass of the body +// (TMoverParameters::ComputeTotalForce and ComputeMovement, much simplified) +void move_body(TMoverParameters &Body, double const Deltatime) +{ + auto const &state{Body.Graph->body()}; + if (Body.TotalMass <= 0.0) + { + return; + } + auto const tractive{state.tractive_force}; + auto const resistance{state.brake_force + Body.FrictionForce()}; + double force; + if (Body.V == 0.0) + { + // standing still until the tractive force overcomes the resistance + force = std::abs(tractive) > resistance ? tractive - (tractive > 0.0 ? resistance : -resistance) : 0.0; + } + else + { + force = tractive - (Body.V > 0.0 ? resistance : -resistance); + } + auto const previous{Body.V}; + Body.AccS = force / Body.TotalMass; + Body.V += Body.AccS * Deltatime; + if (previous != 0.0 && Body.V * previous <= 0.0) + { + // stopped by the brakes and the resistance + Body.V = 0.0; + } + Body.Vel = std::abs(Body.V) * 3.6; + Body.nrot = Body.WheelDiameter > 0.0 ? Body.V / (M_PI * Body.WheelDiameter) : 0.0; + Body.DistCounter += std::abs(Body.V) * Deltatime / 1000.0; + Body.FTrain = tractive; + Body.Fb = state.brake_force; +} + std::string format_number(double const Value) { char buffer[32]; @@ -148,6 +189,141 @@ std::string format_number(double const Value) return buffer; } +// vehicle run by -graph-run: the first one, and the ones coupled behind it (-consist) +struct run_vehicle +{ + std::string path; + bool reversed{false}; // coupled by its rear coupler, facing the other way than the first vehicle + std::shared_ptr graph; + std::unique_ptr body; + std::vector> signals; + bool hasbody{false}; + bool mains{false}; // the graph has several line breakers, so the Mains of the body is written as well + std::string prefix; // of the result columns: empty for the first vehicle, "k:" for the vehicle k +}; + +// path of a vehicle of the consist: as given, or relative to the directory of the first vehicle's file +std::string consist_path(std::string const &Path, std::string const &First) +{ + if (std::ifstream{Path}.is_open()) + { + return Path; + } + auto const relative{std::filesystem::path{First}.parent_path() / Path}; + if (std::filesystem::path{Path}.is_relative() && std::ifstream{relative}.is_open()) + { + return relative.string(); + } + return Path; +} + +bool load_vehicle(run_vehicle &Vehicle) +{ + std::vector diagnostics; + Vehicle.graph = load_graph(Vehicle.path, diagnostics); + for (auto const &entry : diagnostics) + { + std::printf("%s: %s\n", Vehicle.path.c_str(), to_string(entry).c_str()); + } + if (Vehicle.graph == nullptr) + { + return false; + } + auto const &graph{*Vehicle.graph}; + std::printf("%s: %zu blocks, %zu signals, %zu links\n", Vehicle.path.c_str(), graph.nodes().size(), graph.signal_defaults().size(), graph.link_count()); + return true; +} + +// the body of the vehicle, filled from the graph as in the simulator, with the body entries the graph doesn't give +// taken from the .fiz file of the same name, if there is one +bool create_body(run_vehicle &Vehicle, std::string const &Name, bool const Ready, int const Cab) +{ + auto const &input{Vehicle.path}; + auto const &graph{Vehicle.graph}; + Vehicle.body = std::make_unique(0.0, "graph", Name, 1); + auto &body{*Vehicle.body}; + auto fizfile{input}; + fizfile = fizfile.substr(0, std::min(fizfile.size(), fizfile.find_last_of('.'))) + ".fiz"; + if (false == body.AssignGraph(graph, fizfile)) + { + std::printf("%s: the body section or the block parameters were rejected by the vehicle body\n", input.c_str()); + return false; + } + auto const vehicleblocks{std::any_of(std::begin(graph->nodes()), std::end(graph->nodes()), [](auto const &Node) { + auto const &category{Node.type->category}; + return category != "logic" && category != "cab" && category != "indicators" && category != "script" && category != "module"; + })}; + if (vehicleblocks && (body.Dim.L <= 0.0 || body.Mass <= 0.0)) + { + // the simulator doesn't load such a vehicle; the graph itself can still be run here + std::printf("%s: warning: the body has no length or mass (body section, or %s), the simulator won't load the vehicle\n", input.c_str(), fizfile.c_str()); + } + body.ComputeMass(); + body.CheckLocomotiveParameters(Ready, 1); + body.CabActive = Cab; + body.Graph->body().standalone = true; + Vehicle.hasbody = (false == graph->body().empty()); + Vehicle.mains = std::count_if(std::begin(graph->nodes()), std::end(graph->nodes()), [](auto const &Node) { return Node.type->id == "el.hsb"; }) > 1; + Vehicle.signals = graph->signal_outputs(); + return true; +} + +void write_header(std::ostringstream &Results, run_vehicle const &Vehicle) +{ + auto const &graph{*Vehicle.graph}; + auto const &instance{*Vehicle.body->Graph}; + auto const &prefix{Vehicle.prefix}; + for (auto const &signal : Vehicle.signals) + { + Results << ';' << prefix << signal.first; + } + for (std::size_t nodeindex = 0; nodeindex < graph.nodes().size(); ++nodeindex) + { + auto const &item{instance.node_block(nodeindex)}; + for (std::size_t stateindex = 0; stateindex < item.state_count(); ++stateindex) + { + Results << ';' << prefix << graph.nodes()[nodeindex].id << ':' << item.state_name(stateindex); + } + } + if (Vehicle.hasbody) + { + // the vehicle body, for graphs describing whole vehicles + Results << ';' << prefix << "body:v;" << prefix << "body:ft;" << prefix << "body:fb;" << prefix << "body:pg;" << prefix << "body:zg;" << prefix << "body:bc"; + } + if (Vehicle.mains) + { + Results << ';' << prefix << "body:mains"; + } +} + +void write_values(std::ostringstream &Results, run_vehicle const &Vehicle) +{ + auto const &graph{*Vehicle.graph}; + auto const &body{*Vehicle.body}; + auto const &instance{*body.Graph}; + for (auto const &signal : Vehicle.signals) + { + Results << ';' << format_number(instance.signal(signal.second)); + } + for (std::size_t nodeindex = 0; nodeindex < graph.nodes().size(); ++nodeindex) + { + auto const &item{instance.node_block(nodeindex)}; + for (std::size_t stateindex = 0; stateindex < item.state_count(); ++stateindex) + { + Results << ';' << format_number(item.state_value(stateindex)); + } + } + if (Vehicle.hasbody) + { + Results << ';' << format_number(body.Vel) << ';' << format_number(body.FTrain / 1000.0) << ';' << format_number(body.Fb / 1000.0) << ';' << format_number(body.PipePress) + << ';' << format_number(body.Compressor) << ';' << format_number(body.BrakePress); + } + if (Vehicle.mains) + { + Results << ';' << (body.Mains ? 1 : 0); + } +} + } // namespace int graph_catalog_standalone(std::string const &Output) @@ -171,18 +347,22 @@ int graph_catalog_standalone(std::string const &Output) int graph_run_standalone(int const Argc, char *Argv[]) { - // -graph-run [-script ] [-o ] [-dt ] [-t ] [-every ] + // -graph-run [-script ] [-o ] [-dt ] [-t ] [-every ] [-state braked|ready] [-consist [:r]]... if (Argc < 3) { - std::printf("usage: -graph-run [-script ] [-o ] [-dt ] [-t ] [-every ]\n"); + std::printf("usage: -graph-run [-script ] [-o ] [-dt ] [-t ] [-every ] [-state braked|ready] " + "[-consist [:r]]...\n"); return 1; } std::string const input{Argv[2]}; + std::vector vehicles(1); + vehicles.front().path = input; std::string script; std::string output; double deltatime{0.01}; double duration{0.0}; int every{1}; + bool ready{false}; for (int idx = 3; idx + 1 < Argc; idx += 2) { std::string const option{Argv[idx]}; @@ -207,6 +387,20 @@ int graph_run_standalone(int const Argc, char *Argv[]) { every = std::max(1, std::atoi(value.c_str())); } + else if (option == "-state") + { + // initial state of the vehicle body, as for a vehicle placed in a scenario standing (braked) or moving (ready) + ready = (value == "ready"); + } + else if (option == "-consist") + { + // next vehicle of the consist, coupled behind the previous one by its front coupler, or with ":r" reversed (by its rear coupler) + run_vehicle vehicle; + vehicle.reversed = (value.size() > 2 && value.ends_with(":r")); + vehicle.path = consist_path(vehicle.reversed ? value.substr(0, value.size() - 2) : value, input); + vehicle.prefix = std::to_string(vehicles.size() + 1) + ":"; + vehicles.push_back(std::move(vehicle)); + } else { std::printf("unknown option \"%s\"\n", option.c_str()); @@ -219,23 +413,39 @@ int graph_run_standalone(int const Argc, char *Argv[]) return 1; } - std::vector diagnostics; - auto const graph{load_graph(input, diagnostics)}; - for (auto const &entry : diagnostics) + // messages of the lua blocks go straight to the console + set_log_sink([](std::string const &Message) { std::printf("%s\n", Message.c_str()); }); + + for (auto &vehicle : vehicles) { - std::printf("%s: %s\n", input.c_str(), to_string(entry).c_str()); + if (false == load_vehicle(vehicle)) + { + return 1; + } } - if (graph == nullptr) - { - return 1; - } - std::printf("%s: %zu blocks, %zu signals, %zu links\n", input.c_str(), graph->nodes().size(), graph->signal_defaults().size(), graph->link_count()); std::vector events; if (false == script.empty() && false == load_script(script, events)) { return 1; } + for (auto &event : events) + { + // "k:" before the target picks the vehicle k of the consist + auto const separator{event.target.find(':')}; + if (separator == std::string::npos || separator == 0 || event.target.find_first_not_of("0123456789") != separator) + { + continue; + } + auto const number{std::atoi(event.target.substr(0, separator).c_str())}; + if (number < 1 || number > static_cast(vehicles.size())) + { + std::printf("script line %d: there's no vehicle %d in the consist\n", event.line, number); + return 1; + } + event.vehicle = static_cast(number - 1); + event.target.erase(0, separator + 1); + } if (duration <= 0.0 && false == events.empty()) { // by default run a second past the last event @@ -246,21 +456,29 @@ int graph_run_standalone(int const Argc, char *Argv[]) return 0; } - graph_instance instance{graph}; - auto const signals{graph->signal_outputs()}; + for (std::size_t idx = 0; idx < vehicles.size(); ++idx) + { + // the first vehicle has the occupied cab + if (false == create_body(vehicles[idx], idx == 0 ? "graph" : "graph" + std::to_string(idx + 1), ready, idx == 0 ? 1 : 0)) + { + return 1; + } + } + // the consist: every vehicle coupled to the previous one with all the couplings, so the brake pipes connect as well + auto const allcouplings{coupling::coupler | coupling::brakehose | coupling::control | coupling::highvoltage | coupling::gangway | coupling::mainhose | coupling::heating | + coupling::permanent | coupling::power24v | coupling::power110v | coupling::power3x400v}; + for (std::size_t idx = 1; idx < vehicles.size(); ++idx) + { + auto &previous{vehicles[idx - 1]}; + auto &vehicle{vehicles[idx]}; + previous.body->Attach(previous.reversed ? end::front : end::rear, vehicle.reversed ? end::rear : end::front, vehicle.body.get(), allcouplings, true, false); + } + std::ostringstream results; results << "time"; - for (auto const &signal : signals) + for (auto const &vehicle : vehicles) { - results << ';' << signal.first; - } - for (std::size_t nodeindex = 0; nodeindex < graph->nodes().size(); ++nodeindex) - { - auto const &item{instance.node_block(nodeindex)}; - for (std::size_t stateindex = 0; stateindex < item.state_count(); ++stateindex) - { - results << ';' << graph->nodes()[nodeindex].id << ':' << item.state_name(stateindex); - } + write_header(results, vehicle); } results << '\n'; @@ -272,29 +490,29 @@ int graph_run_standalone(int const Argc, char *Argv[]) auto const steptime{step * deltatime}; while (nextevent < events.size() && events[nextevent].time <= steptime + 1e-9) { - if (false == apply_event(instance, events[nextevent])) + auto const &event{events[nextevent]}; + if (false == apply_event(*vehicles[event.vehicle].body->Graph, event)) { return 1; } ++nextevent; } - instance.update(deltatime); + // the vehicles in the order of the consist; each one moves on its own, without the forces of the couplers + for (auto &vehicle : vehicles) + { + auto &body{*vehicle.body}; + body.EqvtPipePress = body.PipePress; + body.compute_movement_graph_(deltatime); + move_body(body, deltatime); + } if ((step + 1) % every != 0 && step + 1 != stepcount) { continue; } results << format_number((step + 1) * deltatime); - for (auto const &signal : signals) + for (auto const &vehicle : vehicles) { - results << ';' << format_number(instance.signal(signal.second)); - } - for (std::size_t nodeindex = 0; nodeindex < graph->nodes().size(); ++nodeindex) - { - auto const &item{instance.node_block(nodeindex)}; - for (std::size_t stateindex = 0; stateindex < item.state_count(); ++stateindex) - { - results << ';' << format_number(item.state_value(stateindex)); - } + write_values(results, vehicle); } results << '\n'; } diff --git a/vehicle/graph/graph_template.cpp b/vehicle/graph/graph_template.cpp index 9efed4c4..b66a732c 100644 --- a/vehicle/graph/graph_template.cpp +++ b/vehicle/graph/graph_template.cpp @@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "vehicle/graph/graph_template.h" #include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/graph_body.h" #include #include @@ -85,6 +86,28 @@ bool resolve_param(param_def const &Definition, param_value const &Value, param_ Result = param_value::make_choice(Value.text); return true; } + case param_type::table: + { + if (Value.type != param_type::table) + { + Problem = "has to be a table: an array of rows, each an array of numbers"; + return false; + } + if (false == Definition.columns.empty()) + { + for (std::size_t rowindex = 0; rowindex < Value.rows.size(); ++rowindex) + { + if (Value.rows[rowindex].size() != Definition.columns.size()) + { + Problem = "row " + std::to_string(rowindex + 1) + " has " + std::to_string(Value.rows[rowindex].size()) + " values, expected " + + std::to_string(Definition.columns.size()) + " (" + join_choices(Definition.columns) + ")"; + return false; + } + } + } + Result = Value; + return true; + } default: { Problem = "has unsupported type"; @@ -107,9 +130,46 @@ std::shared_ptr graph_template::compile(document const &Do auto result{std::make_shared()}; result->m_name = Document.name; result->m_lowvoltage = Document.low_voltage_simulation; - result->m_body = Document.body; result->m_profiles = Document.profiles; + // body of the vehicle + for (auto const &entry : Document.body) + { + auto const *field{find_body_field(entry.first)}; + if (field == nullptr) + { + report(Diagnostics, severity::warning, "", "unknown body parameter \"" + entry.first + "\" was ignored"); + continue; + } + param_def definition; + definition.key = field->key; + definition.type = field->type; + definition.choices = field->choices; + param_value value; + std::string problem; + if (false == resolve_param(definition, entry.second, value, problem)) + { + report(Diagnostics, severity::error, "", "body parameter \"" + entry.first + "\" " + problem); + continue; + } + result->m_body.emplace_back(entry.first, std::move(value)); + } + // interface profiles + for (auto const &profile : Document.profiles) + { + auto const &known{known_profiles()}; + auto const lookup{std::find_if(std::begin(known), std::end(known), [&](profile_info const &Info) { return Info.id == profile.first; })}; + if (lookup == std::end(known)) + { + report(Diagnostics, severity::warning, "", "unknown interface profile \"" + profile.first + "\" was ignored"); + } + else if (profile.second > lookup->version) + { + report(Diagnostics, severity::error, "", + "interface profile \"" + profile.first + "\" version " + std::to_string(profile.second) + " is newer than the supported version " + std::to_string(lookup->version)); + } + } + // nodes and their parameters std::unordered_map nodeindices; std::unordered_map unresolvednodes; // nodes of unknown types, to avoid reporting their connections again @@ -126,21 +186,41 @@ std::shared_ptr graph_template::compile(document const &Do node.id = desc.id; node.title = desc.title; node.type = type; - node.params.reserve(type->params.size()); - for (auto const &definition : type->params) + node.script = desc.script; + if (type->resolve != nullptr) + { + std::string problem; + if (false == type->resolve(desc, node.ports, node.param_defs, problem)) + { + report(Diagnostics, severity::error, desc.id, problem); + unresolvednodes.emplace(desc.id, true); + continue; + } + } + else + { + node.ports = type->ports; + node.param_defs = type->params; + } + if (type->id == "lua.block") + { + result->m_useslua = true; + } + node.params.reserve(node.param_defs.size()); + for (auto const &definition : node.param_defs) { node.params.push_back(definition.default_value); } for (auto const ¶m : desc.params) { - auto const paramindex{type->param_index(param.first)}; + auto const paramindex{find_param(node.param_defs, param.first)}; if (paramindex < 0) { report(Diagnostics, severity::warning, desc.id, "unknown parameter \"" + param.first + "\" of block type \"" + type->id + "\" was ignored"); continue; } std::string problem; - if (false == resolve_param(type->params[paramindex], param.second, node.params[paramindex], problem)) + if (false == resolve_param(node.param_defs[paramindex], param.second, node.params[paramindex], problem)) { report(Diagnostics, severity::error, desc.id, "parameter \"" + param.first + "\" " + problem); } @@ -150,17 +230,33 @@ std::shared_ptr graph_template::compile(document const &Do } auto &nodes{result->m_nodes}; + // blocks standing for parts of the vehicle the simulator has only one of + { + std::unordered_map singles; + for (auto const &node : nodes) + { + if (false == node.type->single) + { + continue; + } + auto const insert{singles.emplace(node.type->id, node.id)}; + if (false == insert.second) + { + report(Diagnostics, severity::error, node.id, "the graph can have only one block of type \"" + node.type->id + "\"; the other one is \"" + insert.first->second + "\""); + } + } + } auto &signaldefaults{result->m_signaldefaults}; auto &linkcount{result->m_linkcount}; // outputs own their slots and links for (auto &node : nodes) { - node.slots.assign(node.type->ports.size(), -1); - node.connected.assign(node.type->ports.size(), 0); - for (std::size_t portindex = 0; portindex < node.type->ports.size(); ++portindex) + node.slots.assign(node.ports.size(), -1); + node.connected.assign(node.ports.size(), 0); + for (std::size_t portindex = 0; portindex < node.ports.size(); ++portindex) { - auto const &port{node.type->ports[portindex]}; + auto const &port{node.ports[portindex]}; if (port.direction != port_direction::out) { continue; @@ -181,7 +277,7 @@ std::shared_ptr graph_template::compile(document const &Do std::vector> sources(nodes.size()); for (std::size_t nodeindex = 0; nodeindex < nodes.size(); ++nodeindex) { - sources[nodeindex].resize(nodes[nodeindex].type->ports.size()); + sources[nodeindex].resize(nodes[nodeindex].ports.size()); } std::vector> edges(nodes.size()); // ordering constraints: source node -> consumer node bool lowvoltageignored{false}; @@ -203,20 +299,22 @@ std::shared_ptr graph_template::compile(document const &Do auto const tonode{tolookup->second}; auto const &fromtype{*nodes[fromnode].type}; auto const &totype{*nodes[tonode].type}; - auto const fromport{fromtype.port_index(connection.from_port)}; - auto const toport{totype.port_index(connection.to_port)}; - if (fromport < 0 || fromtype.ports[fromport].direction != port_direction::out) + auto const &fromports{nodes[fromnode].ports}; + auto const &toports{nodes[tonode].ports}; + auto const fromport{find_port(fromports, connection.from_port)}; + auto const toport{find_port(toports, connection.to_port)}; + if (fromport < 0 || fromports[fromport].direction != port_direction::out) { report(Diagnostics, severity::error, connection.from_node, "connection " + label + ": \"" + connection.from_port + "\" isn't an output of block type \"" + fromtype.id + "\""); continue; } - if (toport < 0 || totype.ports[toport].direction != port_direction::in) + if (toport < 0 || toports[toport].direction != port_direction::in) { report(Diagnostics, severity::error, connection.to_node, "connection " + label + ": \"" + connection.to_port + "\" isn't an input of block type \"" + totype.id + "\""); continue; } - auto const fromdomain{fromtype.ports[fromport].dom}; - auto const todomain{totype.ports[toport].dom}; + auto const fromdomain{fromports[fromport].dom}; + auto const todomain{toports[toport].dom}; if (fromdomain != todomain) { report(Diagnostics, severity::error, connection.to_node, @@ -249,9 +347,9 @@ std::shared_ptr graph_template::compile(document const &Do for (std::size_t nodeindex = 0; nodeindex < nodes.size(); ++nodeindex) { auto &node{nodes[nodeindex]}; - for (std::size_t portindex = 0; portindex < node.type->ports.size(); ++portindex) + for (std::size_t portindex = 0; portindex < node.ports.size(); ++portindex) { - auto const &port{node.type->ports[portindex]}; + auto const &port{node.ports[portindex]}; if (port.direction != port_direction::in) { continue; @@ -379,8 +477,8 @@ int graph_template::find_signal(std::string_view const Endpoint) const return -1; } auto const &node{m_nodes[nodeindex]}; - auto const portindex{node.type->port_index(Endpoint.substr(dot + 1))}; - if (portindex < 0 || node.type->ports[portindex].dom != domain::signal) + auto const portindex{find_port(node.ports, Endpoint.substr(dot + 1))}; + if (portindex < 0 || node.ports[portindex].dom != domain::signal) { return -1; } @@ -394,7 +492,17 @@ std::shared_ptr load_graph(std::string const &Path, std::v { return nullptr; } - return graph_template::compile(graph, catalog::get(), Diagnostics); + auto const separator{Path.find_last_of("/\\")}; + if (false == expand_modules(graph, separator == std::string::npos ? std::string{} : Path.substr(0, separator + 1), Diagnostics)) + { + return nullptr; + } + auto result{graph_template::compile(graph, catalog::get(), Diagnostics)}; + if (result != nullptr) + { + std::const_pointer_cast(result)->m_path = Path; + } + return result; } std::vector> graph_template::signal_outputs() const @@ -402,9 +510,9 @@ std::vector> graph_template::signal_outputs() const std::vector> result; for (auto const &node : m_nodes) { - for (std::size_t portindex = 0; portindex < node.type->ports.size(); ++portindex) + for (std::size_t portindex = 0; portindex < node.ports.size(); ++portindex) { - auto const &port{node.type->ports[portindex]}; + auto const &port{node.ports[portindex]}; if (port.direction == port_direction::out && port.dom == domain::signal) { result.emplace_back(node.id + "." + port.key, node.slots[portindex]); diff --git a/vehicle/graph/graph_template.h b/vehicle/graph/graph_template.h index 9061faa3..7fe43613 100644 --- a/vehicle/graph/graph_template.h +++ b/vehicle/graph/graph_template.h @@ -34,9 +34,12 @@ public: std::string id; std::string title; block_type const *type{nullptr}; - std::vector params; // in the order of the type's parameter definitions - std::vector slots; // in the order of the type's port definitions: signal slot, or link index for physical ports - std::vector connected; // in the order of the type's port definitions: whether the port has a connection + std::vector ports; // the type's ports, or the ones resolved for this node (lua blocks) + std::vector param_defs; // the type's parameters, or the ones resolved for this node + std::vector params; // in the order of param_defs + std::vector slots; // in the order of ports: signal slot, or link index for physical ports + std::vector connected; // in the order of ports: whether the port has a connection + std::string script; // source of a lua block }; // checks the document and builds the template. problems are appended to the diagnostics; returns null on errors @@ -46,6 +49,11 @@ public: { return m_name; } + // the file the graph was loaded from, if any + std::string const &path() const + { + return m_path; + } bool low_voltage_simulation() const { return m_lowvoltage; @@ -83,14 +91,23 @@ public: int find_signal(std::string_view Endpoint) const; // names of signal outputs in "node.port" form, with their slots, in the order of nodes and ports std::vector> signal_outputs() const; + // whether any node needs the lua state + bool uses_lua() const + { + return m_useslua; + } private: + friend std::shared_ptr load_graph(std::string const &Path, std::vector &Diagnostics); + std::string m_name; + std::string m_path; bool m_lowvoltage{false}; std::vector m_nodes; std::vector m_order; std::vector m_signaldefaults; std::size_t m_linkcount{0}; + bool m_useslua{false}; std::vector m_body; std::vector> m_profiles; }; diff --git a/vehicle/graph/graph_train.cpp b/vehicle/graph/graph_train.cpp new file mode 100644 index 00000000..0e469baf --- /dev/null +++ b/vehicle/graph/graph_train.cpp @@ -0,0 +1,276 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "vehicle/Train.h" +#include "vehicle/graph/graph_instance.h" +#include "vehicle/graph/blocks/cab.h" +#include "model/Model3d.h" +#include "utilities/Logs.h" + +// cab of a graph vehicle: its controls and indicators are the cab blocks of the graph. the animated submodels are found +// in the models of the vehicle the same way as for the controls of the .mmd files, and registered with the "graph:" +// labels, which the mouse input turns into the vehiclegraphcontrol command +namespace +{ + +TSubModel *find_submodel(TDynamicObject const &Vehicle, std::string const &Name) +{ + std::array const sources{Vehicle.mdKabina, Vehicle.mdLowPolyInt, Vehicle.mdModel}; + for (auto *source : sources) + { + if (source == nullptr) + { + continue; + } + if (auto *submodel{source->GetFromName(Name)}) + { + return submodel; + } + } + return nullptr; +} + +// whether a block placed in the specified cab ("cab" parameter: 0 any, 1, 2) belongs to the cab with the specified index +// placement from the cab parameter: 0 any cab, 1 or 2, -1 the engine room (cab index 0) +bool in_cab(int const Placement, int const Cabindex) +{ + return Placement == 0 || (Placement < 0 ? Cabindex == 0 : Placement == Cabindex); +} + +user_command find_command(std::string const &Name) +{ + if (Name.empty()) + { + return user_command::none; + } + auto const lookup{simulation::commandMap.find(Name)}; + return lookup != simulation::commandMap.end() ? lookup->second : user_command::none; +} + +} // namespace + +void TTrain::initialize_graph_controls(int const Cabindex) +{ + m_graphcommands.clear(); + auto *graph{mvOccupied->Graph.get()}; + if (graph == nullptr) + { + return; + } + // controls and indicators of the .mmd aren't used by graph vehicles + clear_cab_controls(); + Cabine[Cabindex].ClearControls(); + m_controlmapper.clear(); + + auto const &nodes{graph->definition().nodes()}; + for (std::size_t idx = 0; idx < nodes.size(); ++idx) + { + auto &block{graph->node_block(idx)}; + auto const &id{nodes[idx].id}; + if (auto *control{dynamic_cast(&block)}) + { + // commands of the simulator operate the control in every cab + auto const bind = [&](std::string const &Name, int const Operation) { + auto const command{find_command(Name)}; + if (command == user_command::none) + { + if (false == Name.empty()) + { + ErrorLog("Bad vehicle graph: unknown command \"" + Name + "\" bound to control \"" + id + "\""); + } + return; + } + m_graphcommands[command].push_back({control, Operation}); + }; + bind(control->command(), 0); + bind(control->key_increase(), 1); + bind(control->key_decrease(), -1); + + if (control->submodel().empty() || false == in_cab(control->cab(), Cabindex)) + { + continue; + } + auto *submodel{find_submodel(*DynamicObject, control->submodel())}; + if (submodel == nullptr) + { + ErrorLog("Bad model: failed to locate sub-model \"" + control->submodel() + "\" of control \"" + id + "\" in 3d model(s) of \"" + DynamicObject->name() + "\"", + logtype::model); + continue; + } + // the animation is given in degrees or millimetres per position + auto const rotate{control->animation() == vgraph::cab_switch::animation_type::rotate}; + auto const unit{rotate ? 360.0 : 1000.0}; + auto &gauge{Cabine[Cabindex].Gauge(-1)}; + gauge.AssignSounds(control->sound_increase(), control->sound_decrease(), control->sound_positions(), DynamicObject); + gauge.Init(submodel, nullptr, rotate ? TGaugeAnimation::gt_Rotate : TGaugeAnimation::gt_Move, static_cast(control->step() / unit), + static_cast(control->offset() / unit), static_cast(control->smoothing()), *control->display_state()); + gauge.AssignFloat(control->display_state()); + m_controlmapper.insert(gauge, "graph:" + id); + } + else if (auto *lamp{dynamic_cast(&block)}) + { + if (lamp->submodel().empty() || false == in_cab(lamp->cab(), Cabindex)) + { + continue; + } + auto &button{Cabine[Cabindex].Button(-1)}; + std::array const sources{DynamicObject->mdKabina, DynamicObject->mdLowPolyInt, DynamicObject->mdModel}; + auto found{false}; + for (auto *source : sources) + { + if (button.Init(lamp->submodel(), source, false)) + { + found = true; + break; + } + } + if (false == found) + { + ErrorLog("Bad model: failed to locate sub-model \"" + lamp->submodel() + "\" of lamp \"" + id + "\" in 3d model(s) of \"" + DynamicObject->name() + "\"", + logtype::model); + } + button.AssignBool(lamp->lit_state()); + button.AssignSounds(lamp->sound_on(), lamp->sound_off(), DynamicObject); + } + else if (auto *indicator{dynamic_cast(&block)}) + { + if (false == in_cab(indicator->cab(), Cabindex)) + { + continue; + } + // the needle turns from angle_min at min to angle_max at max + auto const range{indicator->max() - indicator->min()}; + auto const scale{range != 0.0 ? (indicator->angle_max() - indicator->angle_min()) / 360.0 / range : 0.0}; + auto const offset{indicator->angle_min() / 360.0 - indicator->min() * scale}; + for (std::size_t needle = 0; needle < (indicator->two_needles() ? 2 : 1); ++needle) + { + auto const &name{indicator->submodel(needle)}; + if (name.empty()) + { + continue; + } + auto *submodel{find_submodel(*DynamicObject, name)}; + if (submodel == nullptr) + { + ErrorLog("Bad model: failed to locate sub-model \"" + name + "\" of gauge \"" + id + "\" in 3d model(s) of \"" + DynamicObject->name() + "\"", + logtype::model); + continue; + } + auto &gauge{Cabine[Cabindex].Gauge(-1)}; + gauge.Init(submodel, nullptr, TGaugeAnimation::gt_Rotate, static_cast(scale), static_cast(offset), static_cast(indicator->smoothing()), + *indicator->value_state(needle)); + gauge.AssignFloat(indicator->value_state(needle)); + } + } + } +} + +bool TTrain::graph_command(command_data const &Command) +{ + if (Command.command == user_command::vehiclegraphcontrol) + { + // mouse on a control of the graph, or the hardware desk + auto *control{dynamic_cast(mvOccupied->Graph->find_block(Command.payload))}; + if (control == nullptr) + { + return true; + } + auto const increase{Command.param1 >= 0.0}; + if (Command.action == GLFW_RELEASE) + { + control->release(); + } + else if (control->behaviour() == vgraph::cab_switch::behaviour_type::multi) + { + // levers move with every press and repeat + if (increase) + { + control->increase(); + } + else + { + control->decrease(); + } + } + else if (Command.action == GLFW_PRESS) + { + control->press(); + } + return true; + } + auto const lookup{m_graphcommands.find(Command.command)}; + if (lookup == m_graphcommands.end()) + { + return false; + } + for (auto const &binding : lookup->second) + { + auto &control{*binding.control}; + if (Command.action == GLFW_RELEASE) + { + control.release(); + continue; + } + switch (binding.operation) + { + case 1: + { + control.increase(); + break; + } + case -1: + { + control.decrease(); + break; + } + default: + { + if (Command.action == GLFW_PRESS) + { + control.press(); + } + break; + } + } + } + return true; +} + +void TTrain::export_graph_values(dictionary_source &Dictionary) const +{ + auto const *graph{mvOccupied->Graph.get()}; + auto const &nodes{graph->definition().nodes()}; + for (std::size_t idx = 0; idx < nodes.size(); ++idx) + { + auto const *item{dynamic_cast(&graph->node_block(idx))}; + if (item == nullptr || item->name().empty()) + { + continue; + } + switch (item->kind()) + { + case vgraph::cab_export::kind_type::integer: + { + Dictionary.insert(item->name(), static_cast(std::lround(item->value()))); + break; + } + case vgraph::cab_export::kind_type::boolean: + { + Dictionary.insert(item->name(), item->value() > 0.5); + break; + } + default: + { + Dictionary.insert(item->name(), item->value()); + break; + } + } + } +} diff --git a/vehicle/graph/graph_types.cpp b/vehicle/graph/graph_types.cpp index 650456f0..326d10d7 100644 --- a/vehicle/graph/graph_types.cpp +++ b/vehicle/graph/graph_types.cpp @@ -75,6 +75,8 @@ std::string_view to_string(param_type const Type) return "text"; case param_type::choice: return "choice"; + case param_type::table: + return "table"; default: return "number"; } @@ -112,6 +114,38 @@ param_value param_value::make_choice(std::string Value) return result; } +param_value param_value::make_table(std::vector> Rows) +{ + param_value result; + result.type = param_type::table; + result.rows = std::move(Rows); + return result; +} + +int find_port(std::vector const &Ports, std::string_view const Key) +{ + for (std::size_t idx = 0; idx < Ports.size(); ++idx) + { + if (Ports[idx].key == Key) + { + return static_cast(idx); + } + } + return -1; +} + +int find_param(std::vector const &Params, std::string_view const Key) +{ + for (std::size_t idx = 0; idx < Params.size(); ++idx) + { + if (Params[idx].key == Key) + { + return static_cast(idx); + } + } + return -1; +} + bool has_errors(std::vector const &Diagnostics) { return std::any_of(std::begin(Diagnostics), std::end(Diagnostics), [](diagnostic const &Diagnostic) { return Diagnostic.level == severity::error; }); diff --git a/vehicle/graph/graph_types.h b/vehicle/graph/graph_types.h index df98ad64..2bdeced5 100644 --- a/vehicle/graph/graph_types.h +++ b/vehicle/graph/graph_types.h @@ -74,7 +74,8 @@ enum class param_type : std::uint8_t number, boolean, text, - choice + choice, + table // rows of numbers, e.g. the resistor table of a series motor }; std::string_view to_string(param_type Type); @@ -86,11 +87,13 @@ struct param_value double number{0.0}; bool boolean{false}; std::string text; // also holds the choice id + std::vector> rows; // table parameters only static param_value make_number(double Value); static param_value make_boolean(bool Value); static param_value make_text(std::string Value); static param_value make_choice(std::string Value); + static param_value make_table(std::vector> Rows); }; struct port_def @@ -111,6 +114,11 @@ struct param_def std::string unit; param_value default_value; std::vector choices; // choice parameters only, stable ascii ids + std::vector columns; // table parameters only: names of the columns, empty for rows of free width + // .fiz entry the parameter stands for, if any, e.g. "Brake:" and "MaxBP". the simulator writes such parameters + // to the vehicle body (TMoverParameters) with the code reading the .fiz files + std::string fiz_section; + std::string fiz_key; }; enum class severity : std::uint8_t @@ -127,6 +135,10 @@ struct diagnostic std::string message; }; +// index of the port or parameter with the specified key, or -1 +int find_port(std::vector const &Ports, std::string_view Key); +int find_param(std::vector const &Params, std::string_view Key); + bool has_errors(std::vector const &Diagnostics); std::string to_string(diagnostic const &Diagnostic); diff --git a/vehicle/graph/graph_vehicle.cpp b/vehicle/graph/graph_vehicle.cpp new file mode 100644 index 00000000..5a8694b9 --- /dev/null +++ b/vehicle/graph/graph_vehicle.cpp @@ -0,0 +1,465 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "McZapkie/MOVER.h" +#include "vehicle/graph/graph_body.h" +#include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/graph_instance.h" +#include "vehicle/graph/graph_lua.h" +#include "utilities/Logs.h" + +#include +#include +#include +#include + +// vehicle body of a graph vehicle: TMoverParameters filled from the .vhg file. the body section and the parameters of +// the blocks which stand for .fiz entries are written as .fiz lines and read with the code reading the .fiz files, so +// the derived values and the defaults are the same as for a .fiz vehicle with the same entries +namespace +{ + +std::string format_value(double const Value) +{ + char buffer[32]; + std::snprintf(buffer, sizeof(buffer), "%.10g", Value); + return buffer; +} + +// .fiz lines being built, in the order of the sections +class fiz_lines +{ +public: + void add(std::string const &Section, std::string const &Key, std::string const &Value) + { + auto &line{find(Section)}; + line += " " + Key + "=" + Value; + } + bool has(std::string const &Section, std::string const &Key) const + { + for (auto const &entry : m_lines) + { + if (entry.first == Section) + { + return (entry.second + " ").find(" " + Key + "=") != std::string::npos; + } + } + return false; + } + // the line of the section, empty if nothing was added to it + std::string get(std::string const &Section) const + { + for (auto const &entry : m_lines) + { + if (entry.first == Section) + { + return entry.first + entry.second; + } + } + return {}; + } + std::vector> const &lines() const + { + return m_lines; + } + +private: + std::string &find(std::string const &Section) + { + for (auto &entry : m_lines) + { + if (entry.first == Section) + { + return entry.second; + } + } + m_lines.emplace_back(Section, std::string{}); + return m_lines.back().second; + } + + std::vector> m_lines; +}; + +// value of a parameter as written in a .fiz file; values of the .fiz files can't have spaces +std::string fiz_value(vgraph::param_value const &Value, std::vector const &Choices, std::vector const &Fizvalues) +{ + switch (Value.type) + { + case vgraph::param_type::boolean: + return Value.boolean ? "Yes" : "No"; + case vgraph::param_type::number: + return format_value(Value.number); + case vgraph::param_type::choice: + { + if (false == Fizvalues.empty()) + { + for (std::size_t idx = 0; idx < Choices.size() && idx < Fizvalues.size(); ++idx) + { + if (Choices[idx] == Value.text) + { + return Fizvalues[idx]; + } + } + } + return Value.text; + } + default: + { + auto text{Value.text}; + std::replace(std::begin(text), std::end(text), ' ', '_'); + return text; + } + } +} + +// entries of the body sections of a .fiz file which the graph doesn't give. a graph made only of the cab logic, without +// the body section, can this way be tried on an existing vehicle. returns the number of entries taken +int add_fiz_body(fiz_lines &Lines, std::string const &Fizfile) +{ + std::ifstream file{Fizfile, std::ios::binary}; + if (false == file.is_open()) + { + return 0; + } + static char const *const sections[]{"Param.", "Load:", "Dimensions:", "Wheels:", "BuffCoupl.", "Headlights:"}; + int count{0}; + std::string line; + while (std::getline(file, line)) + { + if (false == line.empty() && line.back() == '\r') + { + line.pop_back(); + } + line.erase(0, std::min(line.size(), line.find_first_not_of(" \t"))); + if (line.starts_with("//")) + { + continue; + } + for (auto const *section : sections) + { + if (false == line.starts_with(section)) + { + continue; + } + std::istringstream entries{line.substr(std::string_view{section}.size())}; + std::string entry; + while (entries >> entry) + { + if (entry.starts_with("//")) + { + break; + } + auto const separator{entry.find('=')}; + if (separator == std::string::npos || separator == 0) + { + continue; + } + auto const key{entry.substr(0, separator)}; + if (false == Lines.has(section, key)) + { + Lines.add(section, key, entry.substr(separator + 1)); + ++count; + } + } + break; + } + } + return count; +} + +vgraph::param_value const *node_param(vgraph::graph_template::node_entry const &Node, std::string_view const Key) +{ + auto const index{vgraph::find_param(Node.param_defs, Key)}; + return index >= 0 ? &Node.params[index] : nullptr; +} + +double node_number(vgraph::graph_template::node_entry const &Node, std::string_view const Key, double const Default = 0.0) +{ + auto const *value{node_param(Node, Key)}; + return value != nullptr ? value->number : Default; +} + +bool node_boolean(vgraph::graph_template::node_entry const &Node, std::string_view const Key) +{ + auto const *value{node_param(Node, Key)}; + return value != nullptr && value->boolean; +} + +} // namespace + +bool TMoverParameters::LoadGraph(std::string const &File) +{ + // messages of the graphs (lua blocks) go to the log of the simulator + static bool const logsink{[]() { + vgraph::set_log_sink([](std::string const &Message) { WriteLog(Message); }); + return true; + }()}; + (void)logsink; + + WriteLog("LOAD GRAPH FROM " + File); + std::vector diagnostics; + auto const graph{vgraph::load_graph(File, diagnostics)}; + for (auto const &entry : diagnostics) + { + if (entry.level == vgraph::severity::error) + { + ErrorLog("Bad vehicle graph: " + File + ": " + vgraph::to_string(entry)); + } + else + { + WriteLog("Vehicle graph: " + File + ": " + vgraph::to_string(entry)); + } + } + if (graph == nullptr) + { + ConversionError = 667; + return false; + } + // the .fiz of the same name, if there is one, gives the body entries the graph doesn't have + auto fizfile{File}; + fizfile = fizfile.substr(0, std::min(fizfile.size(), fizfile.find_last_of('.'))) + ".fiz"; + if (false == AssignGraph(graph, fizfile)) + { + return false; + } + // without length the vehicle can't be placed on the track, and without mass it can't move + if (Dim.L <= 0.0 || Mass <= 0.0) + { + ErrorLog("Bad vehicle graph: " + File + ": the body needs the length and the mass (body section of the graph, or the .fiz file of the same name next to it)"); + ConversionError = 668; + return false; + } + return true; +} + +bool TMoverParameters::AssignGraph(std::shared_ptr const &Template, std::string const &Fizfile) +{ + ConversionError = 0; + fiz_lines lines; + + // body + for (auto const &entry : Template->body()) + { + auto const *field{vgraph::find_body_field(entry.first)}; + if (field == nullptr) + { + continue; + } + if (field->key == "light_color") + { + int red{255}, green{255}, blue{255}; + std::sscanf(entry.second.text.c_str(), "%d %d %d", &red, &green, &blue); + lines.add(field->fiz_section, "LampRed", std::to_string(red)); + lines.add(field->fiz_section, "LampGreen", std::to_string(green)); + lines.add(field->fiz_section, "LampBlue", std::to_string(blue)); + continue; + } + lines.add(field->fiz_section, field->fiz_key, fiz_value(entry.second, field->choices, field->fiz_values)); + } + + // equipment parameters of the blocks + bool nativebrakepipe{true}; + bool nativefeedpipe{true}; + for (auto const &node : Template->nodes()) + { + auto const &id{node.type->id}; + for (std::size_t idx = 0; idx < node.param_defs.size(); ++idx) + { + auto const &definition{node.param_defs[idx]}; + if (definition.fiz_key.empty()) + { + continue; + } + auto const &value{node.params[idx]}; + if (id == "br.distributor" && definition.key == "asb" && value.text == "none") + { + continue; + } + lines.add(definition.fiz_section, definition.fiz_key, fiz_value(value, definition.choices, {})); + } + // entries made of several parameters, or implied by the presence of the block + if (id == "br.distributor") + { + lines.add("Cntrl.", "BrakeSystem", "Pneumatic"); + // the number of positions of the brake controller enables the anti-slip brake setting + lines.add("Cntrl.", "BCPN", "1"); + } + else if (id == "br.local_handle") + { + lines.add("Cntrl.", "LocalBrake", "PneumaticBrake"); + } + else if (id == "br.pipe") + { + nativebrakepipe = false; + } + else if (id == "pn.reservoir") + { + nativefeedpipe = false; + } + else if (id == "sec.ca") + { + std::string system; + auto const addflag = [&](char const *Flag) { system += (system.empty() ? "" : ",") + std::string{Flag}; }; + if (node_boolean(node, "aware")) + addflag("Active"); + if (node_boolean(node, "cab_signal")) + addflag("CabSignal"); + if (node_boolean(node, "separate_ack")) + addflag("SeparateAcknowledge"); + if (node_boolean(node, "sifa")) + addflag("Sifa"); + if (false == system.empty()) + { + lines.add("Security:", "AwareSystem", system); + } + auto const minspeed{node_number(node, "min_speed", -1.0)}; + if (minspeed >= 0.0) + { + lines.add("Security:", "AwareMinSpeed", format_value(minspeed)); + } + } + else if (id == "veh.doors") + { + auto const range{node_number(node, "range", 1.0)}; + lines.add("Doors:", "OpenCtrl", "DriverCtrl"); + lines.add("Doors:", "CloseCtrl", "DriverCtrl"); + lines.add("Doors:", "OpenSpeed", format_value(range / std::max(0.01, node_number(node, "open_time", 2.0)))); + lines.add("Doors:", "CloseSpeed", format_value(range / std::max(0.01, node_number(node, "close_time", 3.0)))); + // the graph decides whether the doors have power + lines.add("Doors:", "DoorVoltage", "0"); + } + else if (id == "el.pantograph") + { + lines.add("Power:", "EnginePower", "CurrentCollector"); + } + else if (id == "if.heat") + { + lines.add("Clima:", "Heating", "Main"); + } + } + + if (false == Fizfile.empty()) + { + auto const count{add_fiz_body(lines, Fizfile)}; + if (count > 0) + { + WriteLog("graph: " + std::to_string(count) + " body entries from " + Fizfile); + } + } + + // sections in the order of the .fiz files + auto const read = [&](std::string const &Section, auto Reader) { + auto const line{lines.get(Section)}; + if (false == line.empty()) + { + WriteLog("graph: " + line); + Reader(line); + } + }; + read("Param.", [this](std::string const &Line) { LoadFIZ_Param(Line); }); + read("Load:", [this](std::string const &Line) { LoadFIZ_Load(Line); }); + read("Dimensions:", [this](std::string const &Line) { LoadFIZ_Dimensions(Line); }); + read("Wheels:", [this](std::string const &Line) { LoadFIZ_Wheels(Line); }); + read("Brake:", [this](std::string const &Line) { LoadFIZ_Brake(Line); }); + read("Doors:", [this](std::string const &Line) { LoadFIZ_Doors(Line); }); + read("BuffCoupl.", [this](std::string const &Line) { LoadFIZ_BuffCoupl(Line, 0); }); + read("Cntrl.", [this](std::string const &Line) { LoadFIZ_Cntrl(Line); }); + read("Headlights:", [this](std::string const &Line) { LoadFIZ_Headlights(Line); }); + read("SpringBrake:", [this](std::string const &Line) { LoadFIZ_SpringBrake(Line); }); + read("Security:", [this](std::string const &Line) { SecuritySystem.load(Line, Vmax); }); + read("Clima:", [this](std::string const &Line) { LoadFIZ_Clima(Line); }); + read("Power:", [this](std::string const &Line) { LoadFIZ_Power(Line); }); + + if (EnginePowerSource.SourceType == TPowerSource::CurrentCollector && Power <= 0.0) + { + ErrorLog("Bad vehicle graph: " + Template->path() + ": the pantographs need the body power above zero"); + } + + Graph = std::make_shared(Template); + auto &state{Graph->body()}; + state.mover = this; + state.native_brake_pipe = nativebrakepipe; + state.native_feed_pipe = nativefeedpipe; + return ConversionError == 0; +} + +void TMoverParameters::compute_movement_graph_(double const Deltatime) +{ + auto const dt{Deltatime}; + if (LoadStatus > 0) + { + LastLoadChangeTime += dt; + } + RunInternalCommand(); + + TotalCurrent = 0; + // the pipes take in the flows from the neighbours since the last step, as at the start of UpdatePipePressure and + // UpdateScndPipePressure + if (PipePress > 1.0) + { + Pipe->Flow(-PipePress * AirLeakRate * dt); + Pipe->Act(); + } + PipePress = Pipe->P(); + if (ScndPipePress > 1.0) + { + Pipe2->Flow(-ScndPipePress * AirLeakRate * dt); + Pipe2->Act(); + } + ScndPipePress = Pipe2->P(); + + auto &state{Graph->body()}; + state.velocity = V; + state.acceleration = AccS; + state.wheel_speed = nrot; + state.slipping = SlippingWheels; + state.cab = CabActive; + state.distance = DistCounter * 1000.0; + state.tractive_force = 0.0; + state.brake_force = 0.0; + state.current = 0.0; + + Graph->update(dt); + + // the current of the vehicle, for the pantographs of the vehicle model + Itot = 0.0; + TotalCurrent = state.current; + + // pipes without blocks still connect the neighbours + if (state.native_brake_pipe) + { + Pipe->Flow(GetDVc(dt)); + Pipe->Act(); + PipePress = Pipe->P(); + } + if (state.native_feed_pipe) + { + Pipe2->Flow(ScndPipeExchange(dt)); + Pipe2->Act(); + ScndPipePress = Pipe2->P(); + } + + update_doors(dt); + + PowerCouplersCheck(dt, coupling::highvoltage); + PowerCouplersCheck(dt, coupling::power110v); + PowerCouplersCheck(dt, coupling::power24v); + + Power24vVoltage = std::max(PowerCircuits[0].first, GetTrainsetVoltage(coupling::power24v)); + Power24vIsAvailable = Power24vVoltage > 0; + Power110vIsAvailable = PowerCircuits[1].first > 0 || GetTrainsetVoltage(coupling::power110v) > 0; +} + +void TMoverParameters::graph_forces(double &Tractive, double &Brake) const +{ + auto const &state{Graph->body()}; + Tractive = state.tractive_force; + Brake = state.brake_force; +} diff --git a/widgets/vehiclegraph.cpp b/widgets/vehiclegraph.cpp new file mode 100644 index 00000000..218b265c --- /dev/null +++ b/widgets/vehiclegraph.cpp @@ -0,0 +1,101 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "widgets/vehiclegraph.h" +#include "simulation/simulation.h" +#include "vehicle/Train.h" +#include "vehicle/graph/graph_catalog.h" +#include "vehicle/graph/graph_instance.h" +#include "utilities/translation.h" + +namespace ui +{ + +vehiclegraph_panel::vehiclegraph_panel() : ui_panel(STR("Vehicle graph"), false) +{ + size_min = {400, 200}; +} + +void vehiclegraph_panel::render_contents() +{ + auto *train{simulation::Train}; + auto *graph{train != nullptr ? train->Dynamic()->MoverParameters->Graph.get() : nullptr}; + if (graph == nullptr) + { + ImGui::TextUnformatted(STR_C("The occupied vehicle isn't defined by a vehicle graph")); + return; + } + auto const &definition{graph->definition()}; + auto const &body{graph->body()}; + ImGui::Text("%s t = %.1f s", definition.name().c_str(), graph->time()); + ImGui::Text("Ft %.1f kN Fb %.1f kN I %.0f A", body.tractive_force / 1000.0, body.brake_force / 1000.0, body.current); + ImGui::InputText(STR_C("Filter"), m_filterbuffer, sizeof(m_filterbuffer)); + m_filter = m_filterbuffer; + ImGui::InputDouble(STR_C("Forced value"), &m_forcevalue); + ImGui::Separator(); + + auto const &nodes{definition.nodes()}; + for (std::size_t nodeindex = 0; nodeindex < nodes.size(); ++nodeindex) + { + auto const &node{nodes[nodeindex]}; + if (false == m_filter.empty() && node.id.find(m_filter) == std::string::npos && node.type->id.find(m_filter) == std::string::npos) + { + continue; + } + auto const label{node.id + " (" + node.type->id + ")" + (node.title.empty() ? "" : " - " + node.title)}; + if (false == ImGui::TreeNode(label.c_str())) + { + continue; + } + for (std::size_t portindex = 0; portindex < node.ports.size(); ++portindex) + { + auto const &port{node.ports[portindex]}; + auto const slot{node.slots[portindex]}; + auto const direction{port.direction == vgraph::port_direction::in ? "in " : "out"}; + if (port.dom == vgraph::domain::signal) + { + auto const forced{graph->is_forced(slot)}; + ImGui::Text("%s %-16s %12.4f %s", direction, port.key.c_str(), graph->signal(slot), port.unit.c_str()); + if (port.direction == vgraph::port_direction::out) + { + // forcing a signal output keeps it at the value regardless of its block + ImGui::SameLine(); + auto const button{std::string{forced ? STR_C("release") : STR_C("force")} + "##" + node.id + "." + port.key}; + if (ImGui::SmallButton(button.c_str())) + { + if (forced) + { + graph->release(slot); + } + else + { + graph->force(slot, m_forcevalue); + } + } + } + } + else + { + auto const &link{graph->link(slot)}; + auto const &domain{vgraph::info(port.dom)}; + ImGui::Text("%s %-16s %12.4f %s / %.4f %s", direction, port.key.c_str(), link.forward, std::string{domain.forward_unit}.c_str(), link.back, + std::string{domain.back_unit}.c_str()); + } + } + auto const &block{graph->node_block(nodeindex)}; + for (std::size_t stateindex = 0; stateindex < block.state_count(); ++stateindex) + { + ImGui::Text(" %-16s %12.4f", block.state_name(stateindex), block.state_value(stateindex)); + } + ImGui::TreePop(); + } +} + +} // namespace ui diff --git a/widgets/vehiclegraph.h b/widgets/vehiclegraph.h new file mode 100644 index 00000000..2bc922d0 --- /dev/null +++ b/widgets/vehiclegraph.h @@ -0,0 +1,32 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include "application/uilayer.h" + +namespace ui +{ + +// inspector of the graph of the occupied vehicle, when it's defined by a .vhg file: values of the ports and the internal +// states of the blocks, live, with forcing of the signal values +class vehiclegraph_panel : public ui_panel +{ +public: + vehiclegraph_panel(); + + void render_contents() override; + +private: + std::string m_filter; + char m_filterbuffer[64]{}; + double m_forcevalue{0.0}; +}; + +} // namespace ui