LCOV - code coverage report
Current view: top level - src/microsim/cfmodels - MSCFModel_Rail.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 91.8 % 182 167
Test Date: 2026-07-25 16:16:11 Functions: 90.5 % 21 19

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2012-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    MSCFModel_Rail.cpp
      15              : /// @author  Gregor Laemmel
      16              : /// @author  Leander Flamm
      17              : /// @date    Tue, 08 Feb 2017
      18              : ///
      19              : // <description missing>
      20              : /****************************************************************************/
      21              : #include <config.h>
      22              : 
      23              : #include <iostream>
      24              : #include <utils/common/MsgHandler.h>
      25              : #include <utils/common/StringUtils.h>
      26              : #include <utils/common/StringTokenizer.h>
      27              : #include <utils/geom/GeomHelper.h>
      28              : #include <utils/xml/SUMOSAXAttributes.h>
      29              : #include <microsim/MSVehicle.h>
      30              : #include <microsim/lcmodels/MSAbstractLaneChangeModel.h>
      31              : #include "MSCFModel_Rail.h"
      32              : 
      33              : // ===========================================================================
      34              : // trainParams method definitions
      35              : // ===========================================================================
      36              : 
      37              : double
      38      2658958 : MSCFModel_Rail::TrainParams::getResistance(double speed) const {
      39      2658958 :     if (resCoef_constant != INVALID_DOUBLE) {
      40        20704 :         return (resCoef_quadratic * speed * speed + resCoef_linear * speed + resCoef_constant); // kN
      41              :     } else {
      42      2638254 :         return LinearApproxHelpers::getInterpolatedValue(resistance, speed); // kN
      43              :     }
      44              : }
      45              : 
      46              : 
      47              : double
      48      1714593 : MSCFModel_Rail::TrainParams::getTraction(double speed) const {
      49      1714593 :     if (maxPower != INVALID_DOUBLE) {
      50         6596 :         return MIN2(maxPower / speed, maxTraction); // kN
      51              :     } else {
      52      1707997 :         return LinearApproxHelpers::getInterpolatedValue(traction, speed); // kN
      53              :     }
      54              : }
      55              : 
      56              : 
      57              : // ===========================================================================
      58              : // RailVehicleVariables method definitions
      59              : // ===========================================================================
      60              : void
      61           10 : MSCFModel_Rail::RailVehicleVariables::saveState(OutputDevice& out, const MSCFModel& /*cfm*/) const {
      62           10 :     out.openTag(SUMO_TAG_CFM_VARIABLES);
      63           10 :     out.writeAttr(SUMO_ATTR_ID, "Rail");
      64           10 :     std::ostringstream internals;
      65           10 :     internals << odometerAngles.size() << " ";
      66           28 :     for (auto item : odometerAngles) {
      67           18 :         internals << item.first << " " << item.second << " ";
      68              :     }
      69           10 :     out.writeAttr(SUMO_ATTR_STATE, internals.str());
      70           10 :     out.closeTag();
      71           10 : }
      72              : 
      73              : 
      74              : void
      75           10 : MSCFModel_Rail::RailVehicleVariables::loadState(const SUMOSAXAttributes& attrs) {
      76           10 :     bool ok = true;
      77           10 :     const std::string cfmID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
      78           10 :     if (cfmID != "Rail") {
      79            0 :         throw ProcessError(TLF("incompatible carFollowModel '%' when loading state for Rail", cfmID));
      80              :     }
      81           10 :     std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
      82              :     int odometerAnglesSize;
      83           10 :     bis >> odometerAnglesSize;
      84           28 :     for (int i = 0; i < odometerAnglesSize; i++) {
      85              :         double o;
      86              :         double a;
      87              :         bis >> o;
      88              :         bis >> a;
      89           18 :         odometerAngles.push_back(std::make_pair(o, a));
      90              :     }
      91           20 : }
      92              : 
      93              : 
      94              : 
      95              : double
      96        71550 : MSCFModel_Rail::RailVehicleVariables::getIntegratedRadius(const MSVehicle* veh, double curveIntegration) {
      97        71550 :     const double odo = veh->getOdometer();
      98              :     // add new data point
      99        71550 :     if ((odometerAngles.empty() || odometerAngles.back().first != odo) && veh->hasDeparted()) {
     100         6022 :         odometerAngles.push_back(std::make_pair(odo, veh->getAngle()));
     101              :         // clean up old data points beyond integration distance
     102        11636 :         while (odometerAngles.size() > 2) {
     103        11560 :             double distCleaned = odometerAngles.back().first - odometerAngles[1].first;
     104        11560 :             if (distCleaned >= curveIntegration) {
     105              :                 odometerAngles.erase(odometerAngles.begin());
     106              :             } else {
     107              :                 break;
     108              :             }
     109              :         }
     110              :     }
     111        71550 :     if (odometerAngles.size() > 1) {
     112        70908 :         const double dist = odometerAngles.back().first - odometerAngles.front().first;
     113        70908 :         const double angleDiff = GeomHelper::angleDiff(odometerAngles.back().second, odometerAngles.front().second);
     114              :         return angleDiff == 0
     115        70908 :             ? std::numeric_limits<double>::max()
     116        60448 :             : dist / fabs(angleDiff);
     117              :     } else {
     118          642 :         return veh->getCurveRadius();
     119              :     }
     120              : }
     121              : 
     122              : 
     123              : 
     124              : // ===========================================================================
     125              : // method definitions
     126              : // ===========================================================================
     127              : 
     128              : 
     129          397 : MSCFModel_Rail::MSCFModel_Rail(const MSVehicleType* vtype) :
     130          397 :     MSCFModel(vtype) {
     131          397 :     const std::string trainType = vtype->getParameter().getCFParamString(SUMO_ATTR_TRAIN_TYPE, "NGT400");
     132          397 :     if (trainType.compare("RB425") == 0) {
     133           58 :         myTrainParams = initRB425Params();
     134          339 :     } else if (trainType.compare("RB628") == 0) {
     135           19 :         myTrainParams = initRB628Params();
     136          320 :     } else if (trainType.compare("NGT400") == 0) {
     137          119 :         myTrainParams = initNGT400Params();
     138          201 :     } else if (trainType.compare("NGT400_16") == 0) {
     139            4 :         myTrainParams = initNGT400_16Params();
     140          197 :     } else if (trainType.compare("ICE1") == 0) {
     141            9 :         myTrainParams = initICE1Params();
     142          188 :     } else if (trainType.compare("REDosto7") == 0) {
     143           89 :         myTrainParams = initREDosto7Params();
     144           99 :     } else if (trainType.compare("Freight") == 0) {
     145           74 :         myTrainParams = initFreightParams();
     146           25 :     } else if (trainType.compare("ICE3") == 0) {
     147            5 :         myTrainParams = initICE3Params();
     148           20 :     } else if (trainType.compare("MireoPlusB") == 0) {
     149            4 :         myTrainParams = initMireoPlusB2TParams();
     150           16 :     } else if (trainType.compare("MireoPlusH") == 0) {
     151            4 :         myTrainParams = initMireoPlusH2TParams();
     152           12 :     } else if (trainType.compare("custom") == 0) {
     153           12 :         myTrainParams = initCustomParams();
     154              :     } else {
     155            0 :         WRITE_ERRORF(TL("Unknown train type: %. Exiting!"), trainType);
     156            0 :         throw ProcessError();
     157              :     }
     158              :     // override with user values
     159          397 :     if (vtype->wasSet(VTYPEPARS_MAXSPEED_SET)) {
     160              :         myTrainParams.vmax = vtype->getMaxSpeed();
     161              :     }
     162          397 :     if (vtype->wasSet(VTYPEPARS_LENGTH_SET)) {
     163           68 :         myTrainParams.length = vtype->getLength();
     164              :     }
     165          397 :     myTrainParams.mf = vtype->getParameter().getCFParam(SUMO_ATTR_MASSFACTOR, myTrainParams.mf);
     166          397 :     myTrainParams.decl = vtype->getParameter().getCFParam(SUMO_ATTR_DECEL, myTrainParams.decl);
     167              :     setMaxDecel(myTrainParams.decl);
     168          397 :     setEmergencyDecel(vtype->getParameter().getCFParam(SUMO_ATTR_EMERGENCYDECEL, myTrainParams.decl + 0.3));
     169              :     // update type parameters so they are shown correctly in the gui (if defaults from trainType are used)
     170          397 :     const_cast<MSVehicleType*>(vtype)->setMaxSpeed(myTrainParams.vmax);
     171          397 :     const_cast<MSVehicleType*>(vtype)->setLength(myTrainParams.length);
     172          397 :     if (!vtype->wasSet(VTYPEPARS_MASS_SET)) {
     173              :         // tons to kg
     174          365 :         const_cast<MSVehicleType*>(vtype)->setMass(myTrainParams.weight * 1000);
     175              :     }
     176              : 
     177              :     // init tabular curves
     178          397 :     myTrainParams.traction = vtype->getParameter().getCFProfile(SUMO_ATTR_TRACTION_TABLE, myTrainParams.traction);
     179          397 :     myTrainParams.resistance = vtype->getParameter().getCFProfile(SUMO_ATTR_RESISTANCE_TABLE, myTrainParams.resistance);
     180              : 
     181              :     // init parametric curves
     182          397 :     myTrainParams.maxPower = vtype->getParameter().getCFParam(SUMO_ATTR_MAXPOWER, INVALID_DOUBLE);
     183          397 :     myTrainParams.maxTraction = vtype->getParameter().getCFParam(SUMO_ATTR_MAXTRACTION, INVALID_DOUBLE);
     184          397 :     myTrainParams.resCoef_constant = vtype->getParameter().getCFParam(SUMO_ATTR_RESISTANCE_COEFFICIENT_CONSTANT, INVALID_DOUBLE);
     185          397 :     myTrainParams.resCoef_linear = vtype->getParameter().getCFParam(SUMO_ATTR_RESISTANCE_COEFFICIENT_LINEAR, INVALID_DOUBLE);
     186          397 :     myTrainParams.resCoef_quadratic = vtype->getParameter().getCFParam(SUMO_ATTR_RESISTANCE_COEFFICIENT_QUADRATIC, INVALID_DOUBLE);
     187              :     // curve resistance parameters
     188          397 :     myTrainParams.curveResistance = vtype->getParameter().getCFParam(SUMO_ATTR_CURVE_RESISTANCE, myTrainParams.curveResistance);
     189          397 :     myTrainParams.curveIntegration = vtype->getParameter().getCFParam(SUMO_ATTR_CURVE_INTEGRATION, myTrainParams.curveIntegration);
     190          397 :     myTrainParams.roeckl_sharp_radius = vtype->getParameter().getCFParam(SUMO_ATTR_ROECKL_SHARP_RADIUS, myTrainParams.roeckl_sharp_radius);
     191          397 :     myTrainParams.roeckl_numerator = vtype->getParameter().getCFParam(SUMO_ATTR_ROECKL_NUMERATOR, myTrainParams.roeckl_numerator);
     192          397 :     myTrainParams.roeckl_numerator_sharp = vtype->getParameter().getCFParam(SUMO_ATTR_ROECKL_NUMERATOR_SHARP, myTrainParams.roeckl_numerator_sharp);
     193          397 :     myTrainParams.roeckl_offset = vtype->getParameter().getCFParam(SUMO_ATTR_ROECKL_OFFSET, myTrainParams.roeckl_offset);
     194          397 :     myTrainParams.roeckl_offset_sharp = vtype->getParameter().getCFParam(SUMO_ATTR_ROECKL_OFFSET_SHARP, myTrainParams.roeckl_offset_sharp);
     195              : 
     196          397 :     if (myTrainParams.maxPower != INVALID_DOUBLE && myTrainParams.maxTraction == INVALID_DOUBLE) {
     197            0 :         throw ProcessError(TLF("Undefined maxPower for vType '%'.", vtype->getID()));
     198          397 :     } else if (myTrainParams.maxPower == INVALID_DOUBLE && myTrainParams.maxTraction != INVALID_DOUBLE) {
     199            0 :         throw ProcessError(TLF("Undefined maxTraction for vType '%'.", vtype->getID()));
     200              :     }
     201          409 :     if (myTrainParams.maxPower != INVALID_DOUBLE && vtype->getParameter().getCFParamString(SUMO_ATTR_TRACTION_TABLE, "") != "") {
     202            0 :         WRITE_WARNING(TLF("Ignoring tractionTable because maxPower and maxTraction are set for vType '%'.", vtype->getID()));
     203              :     }
     204          397 :     const bool hasSomeResCoef = (myTrainParams.resCoef_constant != INVALID_DOUBLE
     205          393 :                                  || myTrainParams.resCoef_linear != INVALID_DOUBLE
     206          790 :                                  || myTrainParams.resCoef_quadratic != INVALID_DOUBLE);
     207              :     const bool hasAllResCoef = (myTrainParams.resCoef_constant != INVALID_DOUBLE
     208            4 :                                 && myTrainParams.resCoef_linear != INVALID_DOUBLE
     209          401 :                                 && myTrainParams.resCoef_quadratic != INVALID_DOUBLE);
     210          397 :     if (hasSomeResCoef && !hasAllResCoef) {
     211            0 :         throw ProcessError(TLF("Some undefined resistance coefficients for vType '%' (requires resCoef_constant, resCoef_linear and resCoef_quadratic)", vtype->getID()));
     212              :     }
     213          409 :     if (myTrainParams.resCoef_constant != INVALID_DOUBLE && vtype->getParameter().getCFParamString(SUMO_ATTR_RESISTANCE_TABLE, "") != "") {
     214            4 :         WRITE_WARNING(TLF("Ignoring resistanceTable because resistance coefficients are set for vType '%'.", vtype->getID()));
     215              :     }
     216              : 
     217          397 :     if (myTrainParams.traction.empty() && myTrainParams.maxPower == INVALID_DOUBLE) {
     218           12 :         throw ProcessError(TLF("Either tractionTable or maxPower must be defined for vType '%' with Rail model type '%'.", vtype->getID(), trainType));
     219              :     }
     220          393 :     if (myTrainParams.resistance.empty() && myTrainParams.resCoef_constant == INVALID_DOUBLE) {
     221            0 :         throw ProcessError(TLF("Either resistanceTable or resCoef_constant must be defined for vType '%' with Rail model type '%'.", vtype->getID(), trainType));
     222              :     }
     223          401 : }
     224              : 
     225              : 
     226          784 : MSCFModel_Rail::~MSCFModel_Rail() { }
     227              : 
     228              : 
     229       111100 : double MSCFModel_Rail::followSpeed(const MSVehicle* const veh, double speed, double gap,
     230              :                                    double /* predSpeed */, double /* predMaxDecel*/, const MSVehicle* const /*pred*/, const CalcReason /*usage*/) const {
     231              : 
     232              :     // followSpeed module is used for the simulation of moving block operations. The safety gap is chosen similar to the existing german
     233              :     // system CIR-ELKE (based on LZB). Other implementations of moving block systems may differ, but for now no appropriate parameter
     234              :     // can be set (would be per lane, not per train) -> hard-coded
     235              : 
     236              :     // @note: default train minGap of 5 is already subtracted from gap
     237       111100 :     if (speed >= 30 / 3.6) {
     238              :         // safety distance for higher speeds (>= 30 km/h)
     239        73820 :         gap = MAX2(0.0, gap + veh->getVehicleType().getMinGap() - 50);
     240              :     }
     241              : 
     242       111100 :     const double vsafe = maximumSafeStopSpeed(gap, myDecel, speed, false, TS, false); // absolute breaking distance
     243       111100 :     const double vmin = minNextSpeed(speed, veh);
     244       111100 :     const double vmax = maxNextSpeed(speed, veh);
     245              : 
     246       111100 :     if (MSGlobals::gSemiImplicitEulerUpdate) {
     247              :         return MIN2(vsafe, vmax);
     248              :     } else {
     249              :         // ballistic
     250              :         // XXX: the euler variant can break as strong as it wishes immediately! The ballistic cannot, refs. #2575.
     251              :         return MAX2(MIN2(vsafe, vmax), vmin);
     252              :     }
     253              : }
     254              : 
     255              : 
     256              : int
     257            0 : MSCFModel_Rail::getModelID() const {
     258            0 :     return SUMO_TAG_CF_RAIL;
     259              : }
     260              : 
     261              : 
     262              : MSCFModel*
     263           11 : MSCFModel_Rail::duplicate(const MSVehicleType* vtype) const {
     264           11 :     return new MSCFModel_Rail(vtype);
     265              : }
     266              : 
     267              : double
     268      2658958 : MSCFModel_Rail::getRotWeight(const MSVehicle* const veh) const {
     269      2658958 :     return getWeight(veh) * myTrainParams.mf;
     270              : }
     271              : 
     272              : double
     273      7976874 : MSCFModel_Rail::getWeight(const MSVehicle* const veh) const {
     274              :     // kg to tons
     275      7976874 :     return veh->getVehicleType().getMass() / 1000;
     276              : }
     277              : 
     278              : double
     279      2658958 : MSCFModel_Rail::getCurveResistance(const MSVehicle* veh) const {
     280      2658958 :     if (myTrainParams.curveResistance > 0) {
     281              :         RailVehicleVariables* vars = (RailVehicleVariables*)veh->getCarFollowVariables();
     282              :         assert(vars != nullptr);
     283        71550 :         const double r = vars->getIntegratedRadius(veh, myTrainParams.curveIntegration);
     284        71550 :         if (r == std::numeric_limits<double>::max()) {
     285              :             return 0;
     286        60448 :         } else if (r >= myTrainParams.roeckl_sharp_radius) {
     287        44356 :             return 0.001 * myTrainParams.curveResistance * myTrainParams.roeckl_numerator / (r - myTrainParams.roeckl_offset);
     288        16092 :         } else if (r > myTrainParams.roeckl_offset_sharp) {
     289        13888 :             return 0.001 * myTrainParams.curveResistance * myTrainParams.roeckl_numerator_sharp / (r - myTrainParams.roeckl_offset_sharp);
     290              :         } else {
     291         6612 :             WRITE_WARNINGF("Cannot compute curve resistance for vehicle '%' with radius % at time %",
     292              :                     veh->getID(), r, time2string(SIMSTEP));
     293         2204 :             return 0;
     294              :         }
     295              :     }
     296              :     return 0;
     297              : }
     298              : 
     299              : 
     300      2088772 : double MSCFModel_Rail::maxNextSpeed(double speed, const MSVehicle* const veh) const {
     301              : 
     302      2088772 :     if (speed >= myTrainParams.vmax) {
     303              :         return myTrainParams.vmax;
     304              :     }
     305              : 
     306              :     double targetSpeed = myTrainParams.vmax;
     307              : 
     308      1714593 :     double res = myTrainParams.getResistance(speed); // kN
     309              : 
     310      1714593 :     double slope = veh->getSlope();
     311      1714593 :     double gr = getWeight(veh) * GRAVITY * sin(DEG2RAD(slope)); //kN
     312      1714593 :     double cr = getWeight(veh) * getCurveResistance(veh); //kN
     313              : 
     314      1714593 :     double totalRes = res + gr + cr; //kN
     315              : 
     316      1714593 :     double trac = myTrainParams.getTraction(speed); // kN
     317              :     double a;
     318      1714593 :     if (speed < targetSpeed) {
     319      1714593 :         a = (trac - totalRes) / getRotWeight(veh); //kN/t == N/kg
     320              :     } else {
     321              :         a = 0.;
     322            0 :         if (totalRes > trac) {
     323            0 :             a = (trac - totalRes) / getRotWeight(veh); //kN/t == N/kg
     324              :         }
     325              :     }
     326      1714593 :     double maxNextSpeed = speed + ACCEL2SPEED(a);
     327              : 
     328              : //    std::cout << veh->getID() << " speed: " << (speed*3.6) << std::endl;
     329              : 
     330      1714593 :     return MIN2(myTrainParams.vmax, maxNextSpeed);
     331              : }
     332              : 
     333              : 
     334       944365 : double MSCFModel_Rail::minNextSpeed(double speed, const MSVehicle* const veh) const {
     335              : 
     336       944365 :     const double slope = veh->getSlope();
     337       944365 :     const double gr = getWeight(veh) * GRAVITY * sin(DEG2RAD(slope)); //kN
     338       944365 :     const double cr = getWeight(veh) * getCurveResistance(veh);
     339       944365 :     const double res = myTrainParams.getResistance(speed); // kN
     340       944365 :     const double totalRes = res + gr + cr; //kN
     341       944365 :     const double a = myTrainParams.decl + totalRes / getRotWeight(veh);
     342       944365 :     const double vMin = speed - ACCEL2SPEED(a);
     343       944365 :     if (MSGlobals::gSemiImplicitEulerUpdate) {
     344              :         return MAX2(vMin, 0.);
     345              :     } else {
     346              :         // NOTE: ballistic update allows for negative speeds to indicate a stop within the next timestep
     347              :         return vMin;
     348              :     }
     349              : 
     350              : }
     351              : 
     352              : 
     353              : double
     354       276131 : MSCFModel_Rail::minNextSpeedEmergency(double speed, const MSVehicle* const veh) const {
     355       276131 :     return minNextSpeed(speed, veh);
     356              : }
     357              : 
     358              : 
     359              : //void
     360              : //MSCFModel_Rail::initVehicleVariables(const MSVehicle *const veh, MSCFModel_Rail::VehicleVariables *pVariables) const {
     361              : //
     362              : //    pVariables->setInitialized();
     363              : //
     364              : //}
     365              : 
     366              : 
     367            0 : double MSCFModel_Rail::getSpeedAfterMaxDecel(double /* speed */) const {
     368              : 
     369              : //    //TODO: slope not known here
     370              : //    double gr = 0; //trainParams.weight * GRAVITY * edge.grade
     371              : //
     372              : //    double a = 0;//trainParams.decl - gr/trainParams.rotWeight;
     373              : //
     374              : //    return speed + a * DELTA_T / 1000.;
     375            0 :     WRITE_ERROR("function call not allowed for rail model. Exiting!");
     376            0 :     throw ProcessError();
     377              : }
     378              : 
     379              : 
     380       276131 : double MSCFModel_Rail::finalizeSpeed(MSVehicle* const veh, double vPos) const {
     381       276131 :     return MSCFModel::finalizeSpeed(veh, vPos);
     382              : }
     383              : 
     384              : 
     385      1029759 : double MSCFModel_Rail::freeSpeed(const MSVehicle* const /* veh */, double /* speed */, double dist, double targetSpeed,
     386              :                                  const bool onInsertion, const CalcReason /*usage*/) const {
     387              : 
     388              : //    MSCFModel_Rail::VehicleVariables *vars = (MSCFModel_Rail::VehicleVariables *) veh->getCarFollowVariables();
     389              : //    if (vars->isNotYetInitialized()) {
     390              : //        initVehicleVariables(veh, vars);
     391              : //    }
     392              : 
     393              :     //TODO: signals, coasting, ...
     394              : 
     395      1029759 :     if (MSGlobals::gSemiImplicitEulerUpdate) {
     396              :         // adapt speed to succeeding lane, no reaction time is involved
     397              :         // when breaking for y steps the following distance g is covered
     398              :         // (drive with v in the final step)
     399              :         // g = (y^2 + y) * 0.5 * b + y * v
     400              :         // y = ((((sqrt((b + 2.0*v)*(b + 2.0*v) + 8.0*b*g)) - b)*0.5 - v)/b)
     401      1029758 :         const double v = SPEED2DIST(targetSpeed);
     402      1029758 :         if (dist < v) {
     403              :             return targetSpeed;
     404              :         }
     405       933760 :         const double b = ACCEL2DIST(myDecel);
     406       933760 :         const double y = MAX2(0.0, ((sqrt((b + 2.0 * v) * (b + 2.0 * v) + 8.0 * b * dist) - b) * 0.5 - v) / b);
     407       933760 :         const double yFull = floor(y);
     408       933760 :         const double exactGap = (yFull * yFull + yFull) * 0.5 * b + yFull * v + (y > yFull ? v : 0.0);
     409       933760 :         const double fullSpeedGain = (yFull + (onInsertion ? 1. : 0.)) * ACCEL2SPEED(myTrainParams.decl);
     410      1269729 :         return DIST2SPEED(MAX2(0.0, dist - exactGap) / (yFull + 1)) + fullSpeedGain + targetSpeed;
     411              :     } else {
     412            1 :         WRITE_ERROR(TL("Anything else than semi implicit euler update is not yet implemented. Exiting!"));
     413            1 :         throw ProcessError();
     414              :     }
     415              : }
     416              : 
     417              : 
     418      1094441 : double MSCFModel_Rail::stopSpeed(const MSVehicle* const veh, const double speed, double gap, double decel, const CalcReason /*usage*/) const {
     419      1094441 :     return MIN2(maximumSafeStopSpeed(gap, decel, speed, false, TS, false), maxNextSpeed(speed, veh));
     420              : }
        

Generated by: LCOV version 2.0-1