From 68a45db21ab8d753ad3f438a4da3aed1912cf9b8 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Mon, 2 Sep 2019 19:18:44 +0200 Subject: [PATCH] build 190902. vehicle derailment, oil pressure change tweak, inverter sound tweak, car braking logic tweak, flashing alerter ui indicator, departure signal sources reduction, additional train state data exposed to uart interface, additional train state data for python scripts, wind simulation tweak, coupler animation fix --- Driver.cpp | 13 +- DynObj.cpp | 71 +++++------ DynObj.h | 2 +- Globals.cpp | 6 +- McZapkie/MOVER.h | 10 +- McZapkie/Mover.cpp | 242 +++++++++++++++++++++++++------------- Train.cpp | 12 +- Train.h | 10 +- driveruipanels.cpp | 18 ++- particles.cpp | 4 +- simulationenvironment.cpp | 14 ++- simulationenvironment.h | 3 + station.cpp | 7 +- uart.cpp | 15 ++- uart.h | 3 + version.h | 2 +- 16 files changed, 285 insertions(+), 147 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index b4eab8c0..72b76b03 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -1138,6 +1138,17 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if( sSpeedTable[ i ].fDist > 0.0 ) { // check signals ahead if( sSpeedTable[ i ].IsProperSemaphor( OrderCurrentGet() ) ) { + + if( ( mvOccupied->CategoryFlag & 2 ) + && ( sSpeedTable[ i ].fVelNext != -1.0 ) + && ( sSpeedTable[ i ].fVelNext < 1.0 ) + && ( sSpeedTable[ i ].fDist < -0.5 + std::min( fBrakeDist * 0.2, mvOccupied->Vel * 0.2 ) ) ) { + // special rule for cars: ignore stop signals at distance too short to come to a stop + // as trying to stop in such situation is likely to place the car on train tracks + sSpeedTable[ i ].iFlags &= ~spEnabled; + continue; + } + if( SemNextIndex == -1 ) { // jeśli jest mienięty poprzedni semafor a wcześniej // byl nowy to go dorzucamy do zmiennej, żeby cały czas widział najbliższy @@ -5907,7 +5918,7 @@ TController::UpdateSituation(double dt) { /* mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_FS ) ); GBH */ BrakeLevelSet( gbh_FS ); // don't charge the brakes too often, or we risk overcharging - BrakeChargingCooldown = -120.0; + BrakeChargingCooldown = -1 * clamp( iVehicleCount * 3, 30, 90 ); } } /* diff --git a/DynObj.cpp b/DynObj.cpp index 880ffc83..ba0f1b44 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -849,15 +849,18 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) //*************************************************************/// koniec // wezykow // uginanie zderzakow - for (int i = 0; i < 2; i++) - { - double dist = MoverParameters->Couplers[i].Dist / 2.0; - if (smBuforLewy[i]) - if (dist < 0) - smBuforLewy[i]->SetTranslate( Math3D::vector3(dist, 0, 0)); - if (smBuforPrawy[i]) - if (dist < 0) - smBuforPrawy[i]->SetTranslate( Math3D::vector3(dist, 0, 0)); + for (int i = 0; i < 2; ++i) { + + auto const dist { clamp( MoverParameters->Couplers[ i ].Dist / 2.0, -MoverParameters->Couplers[ i ].DmaxB, 0.0 ) }; + + if( dist >= 0.0 ) { continue; } + + if( smBuforLewy[ i ] ) { + smBuforLewy[ i ]->SetTranslate( Math3D::vector3( dist, 0, 0 ) ); + } + if( smBuforPrawy[ i ] ) { + smBuforPrawy[ i ]->SetTranslate( Math3D::vector3( dist, 0, 0 ) ); + } } } // vehicle within 50m @@ -2204,12 +2207,9 @@ void TDynamicObject::Move(double fDistance) // Ra 2F1J: to nie jest stabilne (powoduje rzucanie taborem) i wymaga // dopracowania fAdjustment = vFront.Length() - fAxleDist; // na łuku będzie ujemny - // if (fabs(fAdjustment)>0.02) //jeśli jest zbyt dużo, to rozłożyć na kilka - // przeliczeń - // (wygasza drgania?) + // if (fabs(fAdjustment)>0.02) //jeśli jest zbyt dużo, to rozłożyć na kilka przeliczeń (wygasza drgania?) //{//parę centymetrów trzeba by już skorygować; te błędy mogą się też - // generować na ostrych - //łukach + // generować na ostrych łukach // fAdjustment*=0.5; //w jednym kroku korygowany jest ułamek błędu //} // else @@ -2784,8 +2784,8 @@ bool TDynamicObject::Update(double dt, double dt1) auto Frj { 0.0 }; auto osie { 0 }; // 0a. ustal aktualna nastawe zadania sily napedowej i hamowania - if (MoverParameters->Power < 1) - { + if( ( MoverParameters->Power < 1 ) + && ( ctOwner != nullptr ) ) { MoverParameters->MainCtrlPos = ctOwner->Controlling()->MainCtrlPos*MoverParameters->MainCtrlPosNo / std::max(1, ctOwner->Controlling()->MainCtrlPosNo); MoverParameters->ScndCtrlActualPos = ctOwner->Controlling()->ScndCtrlActualPos; } @@ -3126,7 +3126,10 @@ bool TDynamicObject::Update(double dt, double dt1) -vPosition.x, vPosition.z, vPosition.y }; - TRotation r { 0.0, 0.0, 0.0 }; + TRotation const r { + 0.0, + 0.0, + modelRot.z }; // McZapkie-260202 - dMoveLen przyda sie przy stukocie kol dDOMoveLen = GetdMoveLen() + MoverParameters->ComputeMovement(dt, dt1, ts, tp, tmpTraction, l, r); if( Mechanik ) @@ -3620,7 +3623,10 @@ bool TDynamicObject::FastUpdate(double dt) -vPosition.x, vPosition.z, vPosition.y }; - TRotation r { 0.0, 0.0, 0.0 }; + TRotation const r { + 0.0, + 0.0, + modelRot.z }; // McZapkie: parametry powinny byc pobierane z toru // ts.R=MyTrack->fRadius; // ts.Len= Max0R(MoverParameters->BDist,MoverParameters->ADist); @@ -3904,7 +3910,7 @@ void TDynamicObject::RenderSounds() { } // NBMX sygnal odjazdu if( MoverParameters->Doors.has_warning ) { - for( auto &door : m_doorsounds ) { + for( auto &departuresignalsound : m_departuresignalsounds ) { // TBD, TODO: per-location door state triggers? if( ( MoverParameters->DepartureSignal ) /* @@ -3915,10 +3921,10 @@ void TDynamicObject::RenderSounds() { ) { // for the autonomous doors play the warning automatically whenever a door is closing // MC: pod warunkiem ze jest zdefiniowane w chk - door.sDepartureSignal.play( sound_flags::exclusive | sound_flags::looping ); + departuresignalsound.play( sound_flags::exclusive | sound_flags::looping ); } else { - door.sDepartureSignal.stop(); + departuresignalsound.stop(); } } } @@ -4227,12 +4233,7 @@ void TDynamicObject::RenderSounds() { if( MoverParameters->EventFlag ) { // McZapkie: w razie wykolejenia if( true == TestFlag( MoverParameters->DamageFlag, dtrain_out ) ) { - if( GetVelocity() > 0 ) { - rsDerailment.play(); - } - else { - rsDerailment.stop(); - } + rsDerailment.play( sound_flags::exclusive ); } MoverParameters->EventFlag = false; @@ -5233,11 +5234,11 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co sound_source soundtemplate { sound_placement::general, 25.f }; soundtemplate.deserialize( parser, sound_type::multipart, sound_parameters::range ); soundtemplate.owner( this ); - for( auto &door : m_doorsounds ) { + for( auto &departuresignalsound : m_departuresignalsounds ) { // apply configuration to all defined doors, but preserve their individual offsets - auto const dooroffset { door.lock.offset() }; - door.sDepartureSignal = soundtemplate; - door.sDepartureSignal.offset( dooroffset ); + auto const soundoffset { departuresignalsound.offset() }; + departuresignalsound = soundtemplate; + departuresignalsound.offset( soundoffset ); } } @@ -5391,7 +5392,6 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co // left... auto const location { glm::vec3 { MoverParameters->Dim.W * 0.5f, MoverParameters->Dim.H * 0.5f, offset } }; door.placement = side::left; - door.sDepartureSignal.offset( location ); door.rsDoorClose.offset( location ); door.rsDoorOpen.offset( location ); door.lock.offset( location ); @@ -5403,9 +5403,8 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co if( ( sides == "both" ) || ( sides == "right" ) ) { // ...and right - auto const location { glm::vec3 { MoverParameters->Dim.W * -0.5f, MoverParameters->Dim.H * 0.5f, offset } }; + auto const location { glm::vec3 { MoverParameters->Dim.W * -0.5f, 2.f, offset } }; door.placement = side::right; - door.sDepartureSignal.offset( location ); door.rsDoorClose.offset( location ); door.rsDoorOpen.offset( location ); door.lock.offset( location ); @@ -5414,6 +5413,10 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co door.step_open.offset( location ); m_doorsounds.emplace_back( door ); } + // potential departure sound, one per door (pair) on vehicle centreline + sound_source departuresignalsound { sound_placement::general, 25.f }; + departuresignalsound.offset( glm::vec3{ 0.f, 3.f, offset } ); + m_departuresignalsounds.emplace_back( departuresignalsound ); } } diff --git a/DynObj.h b/DynObj.h index 77805888..bae28d60 100644 --- a/DynObj.h +++ b/DynObj.h @@ -319,7 +319,6 @@ private: }; struct door_sounds { - sound_source sDepartureSignal { sound_placement::general }; sound_source rsDoorOpen { sound_placement::general }; // Ra: przeniesione z kabiny sound_source rsDoorClose { sound_placement::general }; sound_source lock { sound_placement::general }; @@ -446,6 +445,7 @@ private: std::array m_couplersounds; // always front and rear std::vector m_pantographsounds; // typically 2 but can be less (or more?) std::vector m_doorsounds; // can expect symmetrical arrangement, but don't count on it + std::vector m_departuresignalsounds; // single source per door (pair) on the centreline bool m_doorlocks { false }; // sound helper, current state of door locks sound_source sHorn1 { sound_placement::external, 5 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; sound_source sHorn2 { sound_placement::external, 5 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; diff --git a/Globals.cpp b/Globals.cpp index ac44aaa0..b77a9249 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -649,7 +649,7 @@ global_settings::ConfigParse(cParser &Parser) { >> uart_conf.updatetime; } else if( token == "uarttune" ) { - Parser.getTokens( 14 ); + Parser.getTokens( 16 ); Parser >> uart_conf.mainbrakemin >> uart_conf.mainbrakemax @@ -664,7 +664,9 @@ global_settings::ConfigParse(cParser &Parser) { >> uart_conf.hvmax >> uart_conf.hvuart >> uart_conf.currentmax - >> uart_conf.currentuart; + >> uart_conf.currentuart + >> uart_conf.lvmax + >> uart_conf.lvuart; } else if( token == "uartfeature" ) { Parser.getTokens( 4 ); diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index ad5e1a91..3c4bc493 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -657,6 +657,7 @@ struct TCoupling { double CForce = 0.0; /*sila z jaka dzialal*/ double Dist = 0.0; /*strzalka ugiecia zderzaków*/ bool CheckCollision = false; /*czy sprawdzac sile czy pedy*/ + float stretch_duration { 0.f }; // seconds, elapsed time with excessive force applied to the coupler power_coupling power_high; // power_coupling power_low; // TODO: implement this @@ -1142,6 +1143,7 @@ public: /*--opis konkretnego egzemplarza taboru*/ TLocation Loc { 0.0, 0.0, 0.0 }; //pozycja pojazdów do wyznaczenia odległości pomiędzy sprzęgami TRotation Rot { 0.0, 0.0, 0.0 }; + glm::vec3 Front{}; std::string Name; /*nazwa wlasna*/ TCoupling Couplers[2]; //urzadzenia zderzno-sprzegowe, polaczenia miedzy wagonami std::array Neighbours; // potential collision sources @@ -1407,6 +1409,8 @@ public: bool Attach(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, int CouplingType, bool Forced = false, bool Audible = true); int DettachStatus(int ConnectNo); bool Dettach(int ConnectNo); + void damage_coupler( int const End ); + void derail( int const Reason ); bool DirectionForward(); bool DirectionBackward( void );/*! kierunek ruchu*/ void BrakeLevelSet(double b); @@ -1416,8 +1420,8 @@ public: bool ChangeCab(int direction); bool CurrentSwitch(bool const State); void UpdateBatteryVoltage(double dt); - double ComputeMovement(double dt, double dt1, const TTrackShape &Shape, TTrackParam &Track, TTractionParam &ElectricTraction, const TLocation &NewLoc, TRotation &NewRot); //oblicza przesuniecie pojazdu - double FastComputeMovement(double dt, const TTrackShape &Shape, TTrackParam &Track, const TLocation &NewLoc, TRotation &NewRot); //oblicza przesuniecie pojazdu - wersja zoptymalizowana + double ComputeMovement(double dt, double dt1, const TTrackShape &Shape, TTrackParam &Track, TTractionParam &ElectricTraction, TLocation const &NewLoc, TRotation const &NewRot); //oblicza przesuniecie pojazdu + double FastComputeMovement(double dt, const TTrackShape &Shape, TTrackParam &Track, TLocation const &NewLoc, TRotation const &NewRot); //oblicza przesuniecie pojazdu - wersja zoptymalizowana void compute_movement_( double const Deltatime ); double ShowEngineRotation(int VehN); @@ -1506,7 +1510,7 @@ public: double BrakeForceP(double press, double velocity); double BrakeForce(const TTrackParam &Track); double CouplerForce(int const End, double dt); - void CollisionDetect(int CouplerN, double dt); + void CollisionDetect(int const End, double const dt); /*obrot kol uwzgledniajacy poslizg*/ double ComputeRotatingWheel(double WForce, double dt, double n) const; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index be2cadb2..e25494cd 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -995,68 +995,140 @@ double TMoverParameters::PipeRatio(void) // Q: 20160716 // Wykrywanie kolizji // ************************************************************************************************* -void TMoverParameters::CollisionDetect(int CouplerN, double dt) +void TMoverParameters::CollisionDetect(int const End, double const dt) { - double CCF, Vprev, VprevC; - bool VirtualCoupling; + if( Neighbours[ End ].vehicle == nullptr ) { return; } // shouldn't normally happen but, eh - CCF = 0; - // with Couplers[CouplerN] do - auto &coupler = Couplers[ CouplerN ]; + auto &coupler { Couplers[ End ] }; + auto *othervehicle { Neighbours[ End ].vehicle->MoverParameters }; + auto const otherend { Neighbours[ End ].vehicle_end }; + auto &othercoupler { othervehicle->Couplers[ otherend ] }; - if (coupler.Connected != nullptr) - { - VirtualCoupling = (coupler.CouplingFlag == ctrain_virtual); - Vprev = V; - VprevC = coupler.Connected->V; - switch (CouplerN) - { - case 0: + auto velocity { V }; + auto othervehiclevelocity { othervehicle->V }; + // calculate collision force and new velocities for involved vehicles + auto const VirtualCoupling { ( coupler.CouplingFlag == coupling::faux ) }; + auto CCF { 0.0 }; + + switch( End ) { + case 0: { CCF = ComputeCollision( - V, - coupler.Connected->V, - TotalMass, - coupler.Connected->TotalMass, - (coupler.beta + coupler.Connected->Couplers[coupler.ConnectedNr].beta) / 2.0, - VirtualCoupling) - / (dt); + velocity, othervehiclevelocity, + TotalMass, othervehicle->TotalMass, + ( coupler.beta + othercoupler.beta ) / 2.0, + VirtualCoupling ) + / ( dt ); break; // yB: ej ej ej, a po - case 1: + } + case 1: { CCF = ComputeCollision( - coupler.Connected->V, - V, - coupler.Connected->TotalMass, - TotalMass, - (coupler.beta + coupler.Connected->Couplers[coupler.ConnectedNr].beta) / 2.0, - VirtualCoupling) - / (dt); - break; // czemu tu jest +0.01?? + othervehiclevelocity, velocity, + othervehicle->TotalMass, TotalMass, + ( coupler.beta + othercoupler.beta ) / 2.0, + VirtualCoupling ) + / ( dt ); + break; } - AccS = AccS + (V - Vprev) / dt; // korekta przyspieszenia o siły wynikające ze zderzeń? - coupler.Connected->AccS += (coupler.Connected->V - VprevC) / dt; - if ((coupler.Dist > 0) && (!VirtualCoupling)) - if (FuzzyLogic(abs(CCF), 5.0 * (coupler.FmaxC + 1.0), p_coupldmg)) - { //! zerwanie sprzegu - if (SetFlag(DamageFlag, dtrain_coupling)) - EventFlag = true; + default: { + break; + } + } - if ((coupler.CouplingFlag & ctrain_pneumatic) == ctrain_pneumatic) - AlarmChainFlag = true; // hamowanie nagle - zerwanie przewodow hamulcowych - coupler.CouplingFlag = 0; + if( ( coupler.Dist < 0 ) + && ( FuzzyLogic( std::abs( CCF ), 5.0 * ( coupler.FmaxC + 1.0 ), p_coupldmg ) ) ) { + // small chance to smash the coupler if it's hit with excessive force + damage_coupler( End ); + } - switch (CouplerN) // wyzerowanie flag podlaczenia ale ciagle sa wirtualnie polaczone - { - case 0: - coupler.Connected->Couplers[1].CouplingFlag = 0; - break; - case 1: - coupler.Connected->Couplers[0].CouplingFlag = 0; - break; - } - WriteLog( "Bad driving: " + Name + " broke a coupler" ); + auto const safevelocitylimit { 15.0 }; + auto const velocitydifference { + glm::length( + glm::angleAxis( Rot.Rz, glm::dvec3{ 0, 1, 0 } ) * V + - glm::angleAxis( othervehicle->Rot.Rz, glm::dvec3{ 0, 1, 0 } ) * othervehicle->V ) + * 3.6 }; // m/s -> km/h + + if( velocitydifference > safevelocitylimit ) { + // HACK: crude estimation for potential derail, will take place with velocity difference > 15 km/h adjusted for vehicle mass ratio + WriteLog( "Bad driving: " + Name + " and " + othervehicle->Name + " collided with velocity " + to_string( velocitydifference, 0 ) + " km/h" ); + + if( velocitydifference > safevelocitylimit * ( TotalMass / othervehicle->TotalMass ) ) { + derail( 5 ); + } + if( velocitydifference > safevelocitylimit * ( othervehicle->TotalMass / TotalMass ) ) { + othervehicle->derail( 5 ); + } + } + + // adjust velocity and acceleration of affected vehicles + if( false == TestFlag( DamageFlag, dtrain_out ) ) { + auto const accelerationchange{ ( velocity - V ) / dt }; + // if( accelerationchange / AccS < 1.0 ) { + // HACK: prevent excessive vehicle pinball cases + AccS += accelerationchange; + // AccS = clamp( AccS, -2.0, 2.0 ); + V = velocity; +// } + } + if( false == TestFlag( othervehicle->DamageFlag, dtrain_out ) ) { + auto const othervehicleaccelerationchange{ ( othervehiclevelocity - othervehicle->V ) / dt }; +// if( othervehicleaccelerationchange / othervehicle->AccS < 1.0 ) { + // HACK: prevent excessive vehicle pinball cases + othervehicle->AccS += othervehicleaccelerationchange; + othervehicle->V = othervehiclevelocity; +// } + } +} + +void +TMoverParameters::damage_coupler( int const End ) { + + if( SetFlag( DamageFlag, dtrain_coupling ) ) + EventFlag = true; + + auto &coupler { Couplers[ End ] }; + + if( ( coupler.CouplingFlag & ctrain_pneumatic ) == ctrain_pneumatic ) { + // hamowanie nagle - zerwanie przewodow hamulcowych + AlarmChainFlag = true; + } + + coupler.CouplingFlag = 0; + + if( coupler.Connected != nullptr ) { + switch( End ) { + // break connection with other vehicle, if there's any + case 0: { + coupler.Connected->Couplers[ end::rear ].CouplingFlag = 0; + break; } + case 1: { + coupler.Connected->Couplers[ end::front ].CouplingFlag = 0; + break; + } + default: { + break; + } + } + } + + WriteLog( "Bad driving: " + Name + " broke a coupler" ); +} + +void +TMoverParameters::derail( int const Reason ) { + + if( SetFlag( DamageFlag, dtrain_out ) ) { + + DerailReason = Reason; // TODO: enum derail causes + EventFlag = true; + MainSwitch( false, range_t::local ); + + AccS *= 0.65; + V *= 0.65; + + WriteLog( "Bad driving: " + Name + " derailed" ); } } @@ -1065,7 +1137,7 @@ void TMoverParameters::CollisionDetect(int CouplerN, double dt) // ************************************************************************************************* double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShape &Shape, TTrackParam &Track, TTractionParam &ElectricTraction, - const TLocation &NewLoc, TRotation &NewRot) + TLocation const &NewLoc, TRotation const &NewRot) { const double Vepsilon = 1e-5; const double Aepsilon = 1e-3; // ASBSpeed=0.8; @@ -1099,9 +1171,6 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap // TODO: investigate, seems supplied NewRot is always 0 although the code here suggests some actual values are expected Loc = NewLoc; Rot = NewRot; - NewRot.Rx = 0; - NewRot.Ry = 0; - NewRot.Rz = 0; if (dL == 0) // oblicz przesuniecie} { @@ -1230,17 +1299,14 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap // ************************************************************************************************* double TMoverParameters::FastComputeMovement(double dt, const TTrackShape &Shape, - TTrackParam &Track, const TLocation &NewLoc, - TRotation &NewRot) + TTrackParam &Track, TLocation const &NewLoc, + TRotation const &NewRot) { int b; // T_MoverParameters::FastComputeMovement(dt, Shape, Track, NewLoc, NewRot); Loc = NewLoc; Rot = NewRot; - NewRot.Rx = 0.0; - NewRot.Ry = 0.0; - NewRot.Rz = 0.0; if (dL == 0) // oblicz przesuniecie { @@ -1716,7 +1782,7 @@ void TMoverParameters::OilPumpCheck( double const Timestep ) { OilPump.pressure = std::max( OilPump.pressure_target, - OilPump.pressure - 0.01 * Timestep ); + OilPump.pressure - ( enrot > 5.0 ? 0.05 : 0.035 ) * 0.5 * Timestep ); } OilPump.pressure = clamp( OilPump.pressure, 0.f, 1.5f ); } @@ -4129,6 +4195,11 @@ void TMoverParameters::ComputeTotalForce(double dt) { // juz zoptymalizowane: FStand = FrictionForce(RunningShape.R, RunningTrack.DamageFlag); // siła oporów ruchu + if( true == TestFlag( DamageFlag, dtrain_out ) ) { + // HACK: crude way to reduce speed after derailment + // TBD, TODO: more accurate approach? + FStand *= 1e20; + } double old_nrot = abs(nrot); nrot = v2n(); // przeliczenie prędkości liniowej na obrotową @@ -4418,15 +4489,13 @@ double TMoverParameters::CouplerForce( int const End, double dt ) { auto &othercoupler { othervehicle->Couplers[ otherend ] }; auto const othervehiclemove { ( othervehicle->dMoveLen * DirPatch( End, otherend ) ) }; + auto const initialdistance { Neighbours[ End ].distance }; // odległość od sprzęgu sąsiada auto const distancedelta { ( End == end::front ? othervehiclemove - dMoveLen : dMoveLen - othervehiclemove ) }; - auto const initialdistance { Neighbours[ End ].distance }; // odległość od sprzęgu sąsiada - auto const newdistance = - initialdistance - + 10.0 * distancedelta; + auto const newdistance { initialdistance + 10.0 * distancedelta }; auto const dV { V - ( othervehicle->V * DirPatch( End, otherend ) ) }; auto const absdV { std::abs( dV ) }; @@ -4456,11 +4525,15 @@ double TMoverParameters::CouplerForce( int const End, double dt ) { } coupler.CheckCollision = false; + coupler.Dist = 0.0; + double CF { 0.0 }; if( ( coupler.CouplingFlag != coupling::faux ) || ( initialdistance < 0 ) ) { + coupler.Dist = clamp( newdistance, -coupler.DmaxB, coupler.DmaxC ); + double BetaAvg = 0; double Fmax = 0; @@ -4475,8 +4548,8 @@ double TMoverParameters::CouplerForce( int const End, double dt ) { Fmax = 0.5 * ( coupler.FmaxC + coupler.FmaxB + othercoupler.FmaxC + othercoupler.FmaxB ) * CouplerTune; } auto const distDelta { std::abs( newdistance ) - std::abs( coupler.Dist ) }; // McZapkie-191103: poprawka na histereze - coupler.Dist = newdistance; - if (coupler.Dist > 0) { + + if (newdistance > 0) { if( distDelta > 0 ) { CF = ( -( coupler.SpringKC + othercoupler.SpringKC ) * coupler.Dist / 2.0 ) * DirF( End ) @@ -4487,12 +4560,25 @@ double TMoverParameters::CouplerForce( int const End, double dt ) { - Fmax * dV * BetaAvg; } // liczenie sily ze sprezystosci sprzegu - if( coupler.Dist > ( coupler.DmaxC + othercoupler.DmaxC ) ) { + if( newdistance > ( coupler.DmaxC + othercoupler.DmaxC ) ) { // zderzenie coupler.CheckCollision = true; } + if( std::abs( CF ) > coupler.FmaxC ) { + // coupler is stretched with excessive force, may break + coupler.stretch_duration += dt; + // give coupler 1 sec of leeway to account for simulation glitches, before checking whether it breaks + // (arbitrary) chance to break grows from 10-100% over 10 sec period + if( ( coupler.stretch_duration > 1.f ) + && ( Random() < ( coupler.stretch_duration * 0.1f * dt ) ) ) { + damage_coupler( End ); + } + } + else { + coupler.stretch_duration = 0.f; + } } - if( coupler.Dist < 0 ) { + if( newdistance < 0 ) { if( distDelta > 0 ) { CF = ( -( coupler.SpringKB + othercoupler.SpringKB ) * coupler.Dist / 2.0 ) * DirF( End ) @@ -4503,7 +4589,7 @@ double TMoverParameters::CouplerForce( int const End, double dt ) { - Fmax * dV * BetaAvg; } // liczenie sily ze sprezystosci zderzaka - if( -coupler.Dist > ( coupler.DmaxB + othercoupler.DmaxB ) ) { + if( -newdistance > ( coupler.DmaxB + othercoupler.DmaxB ) ) { // zderzenie coupler.CheckCollision = true; if( ( coupler.CouplerType == TCouplerType::Automatic ) @@ -5402,25 +5488,19 @@ double TMoverParameters::TractionForce( double dt ) { eimv[eimv_ks] = eimv[eimv_Fr] / eimv[eimv_FMAXMAX]; eimv[eimv_df] = eimv[eimv_ks] * eimc[eimc_s_dfmax]; - eimv[eimv_fp] = DirAbsolute * enrot * eimc[eimc_s_p] + - eimv[eimv_df]; // do przemyslenia dzialanie pp z tmpV + eimv[eimv_fp] = DirAbsolute * enrot * eimc[eimc_s_p] + eimv[eimv_df]; // do przemyslenia dzialanie pp z tmpV // eimv[eimv_U]:=Max0R(eimv[eimv_Uzsmax],Min0R(eimc[eimc_f_cfu]*eimv[eimv_fp],eimv[eimv_Uzsmax])); // eimv[eimv_pole]:=eimv[eimv_U]/(eimv[eimv_fp]*eimc[eimc_s_cfu]); if ((abs(eimv[eimv_fp]) <= eimv[eimv_fkr])) eimv[eimv_pole] = eimc[eimc_f_cfu] / eimc[eimc_s_cfu]; else - eimv[eimv_pole] = - eimv[eimv_Uzsmax] / eimc[eimc_s_cfu] / abs(eimv[eimv_fp]); + eimv[eimv_pole] = eimv[eimv_Uzsmax] / eimc[eimc_s_cfu] / abs(eimv[eimv_fp]); eimv[eimv_U] = eimv[eimv_pole] * eimv[eimv_fp] * eimc[eimc_s_cfu]; - eimv[eimv_Ic] = (eimv[eimv_fp] - DirAbsolute * enrot * eimc[eimc_s_p]) * - eimc[eimc_s_dfic] * eimv[eimv_pole]; + eimv[eimv_Ic] = (eimv[eimv_fp] - DirAbsolute * enrot * eimc[eimc_s_p]) * eimc[eimc_s_dfic] * eimv[eimv_pole]; eimv[eimv_If] = eimv[eimv_Ic] * eimc[eimc_s_icif]; eimv[eimv_M] = eimv[eimv_pole] * eimv[eimv_Ic] * eimc[eimc_s_cim]; - eimv[eimv_Ipoj] = (eimv[eimv_Ic] * NPoweredAxles * eimv[eimv_U]) / - (Voltage - eimc[eimc_f_DU]) + - eimc[eimc_f_I0]; - eimv[eimv_Pm] = - ActiveDir * eimv[eimv_M] * NPoweredAxles * enrot * Pirazy2 / 1000; + eimv[eimv_Ipoj] = (eimv[eimv_Ic] * NPoweredAxles * eimv[eimv_U]) / (Voltage - eimc[eimc_f_DU]) + eimc[eimc_f_I0]; + eimv[eimv_Pm] = ActiveDir * eimv[eimv_M] * NPoweredAxles * enrot * Pirazy2 / 1000; eimv[eimv_Pe] = eimv[eimv_Ipoj] * Voltage / 1000; eimv[eimv_eta] = eimv[eimv_Pm] / eimv[eimv_Pe]; @@ -5441,7 +5521,7 @@ double TMoverParameters::TractionForce( double dt ) { if( ( RlistSize > 0 ) && ( ( std::abs( eimv[ eimv_If ] ) > 1.0 ) - || ( tmpV > 0.1 ) ) ) { + && ( tmpV > 0.1 ) ) ) { int i = 0; while( ( i < RlistSize - 1 ) diff --git a/Train.cpp b/Train.cpp index 3ab853f1..73095609 100644 --- a/Train.cpp +++ b/Train.cpp @@ -500,7 +500,7 @@ dictionary_source *TTrain::GetTrainState() { // induction motor state data char const *TXTT[ 10 ] = { "fd", "fdt", "fdb", "pd", "pdt", "pdb", "itothv", "1", "2", "3" }; char const *TXTC[ 10 ] = { "fr", "frt", "frb", "pr", "prt", "prb", "im", "vm", "ihv", "uhv" }; - char const *TXTD[ 10 ] = { "enrot", "nrot", "fill_des", "fill_real", "clutch_des", "clutch_real", "water_temp", "oil_press", "res1", "res2" }; + char const *TXTD[ 10 ] = { "enrot", "nrot", "fill_des", "fill_real", "clutch_des", "clutch_real", "water_temp", "oil_press", "engine_temp", "res1" }; char const *TXTP[ 3 ] = { "bc", "bp", "sp" }; for( int j = 0; j < 10; ++j ) dict->insert( ( "eimp_t_" + std::string( TXTT[ j ] ) ), fEIMParams[ 0 ][ j ] ); @@ -588,6 +588,7 @@ TTrain::get_state() const { btLampkaNadmWent.GetValue(), btLampkaWysRozr.GetValue(), btLampkaOgrzewanieSkladu.GetValue(), + static_cast( iCabn ), btHaslerBrakes.GetValue(), btHaslerCurrent.GetValue(), ( TestFlag( mvOccupied->SecuritySystem.Status, s_CAalarm ) || TestFlag( mvOccupied->SecuritySystem.Status, s_SHPalarm ) ), @@ -597,7 +598,8 @@ TTrain::get_state() const { static_cast( mvOccupied->PipePress ), static_cast( mvOccupied->BrakePress ), fHVoltage, - { fHCurrent[ ( mvControlled->TrainType & dt_EZT ) ? 0 : 1 ], fHCurrent[ 2 ], fHCurrent[ 3 ] } + { fHCurrent[ ( mvControlled->TrainType & dt_EZT ) ? 0 : 1 ], fHCurrent[ 2 ], fHCurrent[ 3 ] }, + ggLVoltage.GetValue() }; } @@ -5375,7 +5377,9 @@ bool TTrain::Update( double const Deltatime ) in++; iPowerNo = in; } - if ((in < 8) && (p->MoverParameters->EngineType==TEngineType::DieselEngine)) + if ((in < 8) + && ((p->MoverParameters->EngineType==TEngineType::DieselEngine) + ||(p->MoverParameters->EngineType==TEngineType::DieselElectric))) { fDieselParams[1 + in][0] = p->MoverParameters->enrot*60; fDieselParams[1 + in][1] = p->MoverParameters->nrot; @@ -5385,7 +5389,7 @@ bool TTrain::Update( double const Deltatime ) fDieselParams[1 + in][5] = p->MoverParameters->dizel_engage; fDieselParams[1 + in][6] = p->MoverParameters->dizel_heat.Twy; fDieselParams[1 + in][7] = p->MoverParameters->OilPump.pressure; - //fDieselParams[1 + in][8] = p->MoverParameters-> + fDieselParams[1 + in][8] = p->MoverParameters->dizel_heat.Ts; //fDieselParams[1 + in][9] = p->MoverParameters-> bMains[in] = p->MoverParameters->Mains; fCntVol[in] = p->MoverParameters->BatteryVoltage; diff --git a/Train.h b/Train.h index 0c510ef6..62a44097 100644 --- a/Train.h +++ b/Train.h @@ -69,8 +69,10 @@ public: find( TSubModel const *Control ) const; }; -class TTrain -{ +class TTrain { + + friend class drivingaid_panel; + public: // types struct state_t { @@ -88,6 +90,7 @@ class TTrain std::uint8_t ventilator_overload; std::uint8_t motor_overload_threshold; std::uint8_t train_heating; + std::uint8_t cab; std::uint8_t recorder_braking; std::uint8_t recorder_power; std::uint8_t alerter_sound; @@ -98,6 +101,7 @@ class TTrain float brake_pressure; float hv_voltage; std::array hv_current; + float lv_voltage; }; // methods @@ -626,7 +630,7 @@ public: // reszta może by?publiczna */ // McZapkie: opis kabiny - obszar poruszania sie mechanika oraz zajetosc std::array Cabine; // przedzial maszynowy, kabina 1 (A), kabina 2 (B) - int iCabn { 0 }; + int iCabn { 0 }; // 0: mid, 1: front, 2: rear // McZapkie: do poruszania sie po kabinie Math3D::vector3 pMechSittingPosition; // ABu 180404 Math3D::vector3 MirrorPosition( bool lewe ); diff --git a/driveruipanels.cpp b/driveruipanels.cpp index 4845d6d6..96ef1b98 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -20,6 +20,7 @@ http://mozilla.org/MPL/2.0/. #include "Camera.h" #include "mtable.h" #include "Train.h" +#include "Button.h" #include "Driver.h" #include "AnimModel.h" #include "DynObj.h" @@ -135,12 +136,14 @@ drivingaid_panel::update() { } } } - std::string textline = - ( true == TestFlag( mover->SecuritySystem.Status, s_aware ) ? + std::string textline = ( + ( ( true == TestFlag( mover->SecuritySystem.Status, s_aware ) ) + && ( train != nullptr ) + && ( train->fBlinkTimer > 0 ) ) ? locale::strings[ locale::string::driver_aid_alerter ] : " " ); - textline += - ( true == TestFlag( mover->SecuritySystem.Status, s_active ) ? + textline += ( + ( true == TestFlag( mover->SecuritySystem.Status, s_active ) ) ? locale::strings[ locale::string::driver_aid_shp ] : " " ); @@ -961,6 +964,13 @@ debug_panel::update_section_scenario( std::vector &Output ) { textline = "Cloud cover: " + to_string( Global.Overcast, 3 ); textline += "\nLight level: " + to_string( Global.fLuminance, 3 ); if( Global.FakeLight ) { textline += "(*)"; } + textline += + "\nWind: azimuth " + + to_string( simulation::Environment.wind_azimuth(), 0 ) // ma być azymut, czyli 0 na północy i rośnie na wschód + + " " + + std::string( "N NEE SES SWW NW" ) + .substr( 0 + 2 * std::floor( std::fmod( 8 + ( glm::radians( simulation::Environment.wind_azimuth() ) + 0.5 * M_PI_4 ) / M_PI_4, 8 ) ), 2 ) + + ", " + to_string( glm::length( simulation::Environment.wind() ), 1 ) + " m/s"; textline += "\nAir temperature: " + to_string( Global.AirTemperature, 1 ) + " deg C"; Output.emplace_back( textline, Global.UITextColor ); diff --git a/particles.cpp b/particles.cpp index 619289d5..23ab6b5c 100644 --- a/particles.cpp +++ b/particles.cpp @@ -61,9 +61,9 @@ smoke_source::particle_emitter::initialize( smoke_particle &Particle ) { auto const azimuthalangle { glm::radians( Random( -180, 180 ) ) }; // phi // convert spherical coordinates to opengl coordinates auto const launchvector { glm::vec3( - std::sin( polarangle ) * std::sin( azimuthalangle ), + std::sin( polarangle ) * std::sin( azimuthalangle ) * -1, std::cos( polarangle ), - std::sin( polarangle ) * std::cos( azimuthalangle ) * -1 ) }; + std::sin( polarangle ) * std::cos( azimuthalangle ) ) }; auto const launchvelocity { static_cast( Random( velocity[ value_limit::min ], velocity[ value_limit::max ] ) ) }; Particle.velocity = launchvector * launchvelocity; diff --git a/simulationenvironment.cpp b/simulationenvironment.cpp index 6b5833c8..d8fddb50 100644 --- a/simulationenvironment.cpp +++ b/simulationenvironment.cpp @@ -194,13 +194,17 @@ world_environment::update_wind() { m_wind.change_time -= timedelta; if( m_wind.change_time < 0 ) { - m_wind.change_time = Random( 5, 30 ); - m_wind.velocity_change = Random( -1, 1 ); - if( Random() < 0.2 ) { + m_wind.change_time = Random( 5, 15 ); + m_wind.velocity_change = Random( -0.2, 0.2 ); + if( Random() < 0.05 ) { // changes in wind direction should be less frequent than changes in wind speed // TBD, TODO: configuration-driven direction change frequency m_wind.azimuth_change = Random( -5, 5 ); } + else { + // keep direction change periods short, to avoid too drastic changes in direction + m_wind.azimuth_change = 0.0; + } } // TBD, TODO: wind configuration m_wind.azimuth = clamp_circular( m_wind.azimuth + m_wind.azimuth_change * timedelta ); @@ -213,9 +217,9 @@ world_environment::update_wind() { m_wind.vector = std::max( 0.f, m_wind.velocity ) * glm::vec3( - std::sin( polarangle ) * std::sin( azimuthalangle ), + std::sin( polarangle ) * std::sin( azimuthalangle ) * -1, std::cos( polarangle ), - std::sin( polarangle ) * std::cos( azimuthalangle ) * -1 ); + std::sin( polarangle ) * std::cos( azimuthalangle ) ); } void diff --git a/simulationenvironment.h b/simulationenvironment.h index 09834c99..69f521c2 100644 --- a/simulationenvironment.h +++ b/simulationenvironment.h @@ -40,6 +40,9 @@ public: glm::vec3 const & wind() const { return m_wind.vector; } + float const & + wind_azimuth() const { + return m_wind.azimuth; } private: // types diff --git a/station.cpp b/station.cpp index b13287fd..359785b1 100644 --- a/station.cpp +++ b/station.cpp @@ -56,15 +56,16 @@ basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Sch // NOTE: for the time being we're doing simple, random load change calculation // TODO: exchange driven by station parameters and time of the day auto unloadcount = static_cast( + TestFlag( parameters.DamageFlag, dtrain_out ) ? parameters.LoadAmount : laststop ? parameters.LoadAmount : firststop ? 0 : std::min( parameters.LoadAmount, Random( parameters.MaxLoad * 0.15f * stationsizemodifier ) ) ); auto loadcount = static_cast( - laststop ? - 0 : - Random( parameters.MaxLoad * 0.15f * stationsizemodifier ) ); + TestFlag( parameters.DamageFlag, dtrain_out ) ? 0 : + laststop ? 0 : + Random( parameters.MaxLoad * 0.15f * stationsizemodifier ) ); if( true == firststop ) { // larger group at the initial station loadcount *= 2; diff --git a/uart.cpp b/uart.cpp index 55d3b56d..35dd0c0e 100644 --- a/uart.cpp +++ b/uart.cpp @@ -242,6 +242,12 @@ void uart_input::poll() uint16_t current1 = (uint16_t)std::min(conf.currentuart, trainstate.hv_current[0] / conf.currentmax * conf.currentuart); uint16_t current2 = (uint16_t)std::min(conf.currentuart, trainstate.hv_current[1] / conf.currentmax * conf.currentuart); uint16_t current3 = (uint16_t)std::min(conf.currentuart, trainstate.hv_current[2] / conf.currentmax * conf.currentuart); + uint16_t lv_voltage = (uint16_t)std::min( conf.lvuart, trainstate.lv_voltage / conf.lvmax * conf.lvuart ); + + if( trainstate.cab > 0 ) { + // NOTE: moving from a cab to engine room doesn't change cab indicator + m_trainstatecab = trainstate.cab - 1; + } std::array buffer { //byte 0 @@ -271,7 +277,8 @@ void uart_input::poll() | trainstate.compressor_overload << 6), //byte 6 (uint8_t)( - trainstate.recorder_braking << 3 + m_trainstatecab << 2 + | trainstate.recorder_braking << 3 | trainstate.recorder_power << 4 | trainstate.radio_stop <<5 | trainstate.alerter_sound << 7), @@ -289,8 +296,10 @@ void uart_input::poll() SPLIT_INT16(current2), //byte 19-20 SPLIT_INT16(current3), - //byte 21-30 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + //byte 21-22 + SPLIT_INT16(lv_voltage), + //byte 23-30 + 0, 0, 0, 0, 0, 0, 0, 0 }; if (conf.debug) diff --git a/uart.h b/uart.h index b6e1dd6b..a1887353 100644 --- a/uart.h +++ b/uart.h @@ -27,6 +27,8 @@ public: float hvuart = 65535.0f; float currentmax = 10000.0f; float currentuart = 65535.0f; + float lvmax = 150.0f; + float lvuart = 65535.0f; bool mainenable = true; bool scndenable = true; @@ -66,4 +68,5 @@ private: std::chrono::time_point last_update; conf_t conf; bool data_pending = false; + std::uint8_t m_trainstatecab { 0 }; // helper, keeps track of last active cab. 0: front cab, 1: rear cab }; diff --git a/version.h b/version.h index 1f1a9c5b..f159e50b 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 19 -#define VERSION_MINOR 809 +#define VERSION_MINOR 902 #define VERSION_REVISION 0