LCOV - code coverage report
Current view: top level - src/microsim/devices - MSDevice_GLOSA.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 77.8 % 248 193
Test Date: 2024-11-22 15:46:21 Functions: 77.8 % 18 14

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2013-2024 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    MSDevice_GLOSA.cpp
      15              : /// @author  Jakob Erdmann
      16              : /// @author  Mirko Barthauer
      17              : /// @date    21.04.2021
      18              : ///
      19              : // A device for Green Light Optimal Speed Advisory
      20              : /****************************************************************************/
      21              : #include <config.h>
      22              : 
      23              : #include <utils/common/StringUtils.h>
      24              : #include <utils/options/OptionsCont.h>
      25              : #include <utils/iodevices/OutputDevice.h>
      26              : #include <utils/vehicle/SUMOVehicle.h>
      27              : #include <microsim/traffic_lights/MSTrafficLightLogic.h>
      28              : #include <microsim/MSNet.h>
      29              : #include <microsim/MSLane.h>
      30              : #include <microsim/MSEdge.h>
      31              : #include <microsim/MSLink.h>
      32              : #include <microsim/MSVehicle.h>
      33              : #include "MSDevice_GLOSA.h"
      34              : 
      35              : //#define DEBUG_GLOSA
      36              : //#define DEBUG_QUEUE
      37              : #define DEBUG_COND (true)
      38              : 
      39              : // ===========================================================================
      40              : // method definitions
      41              : // ===========================================================================
      42              : // ---------------------------------------------------------------------------
      43              : // static initialisation methods
      44              : // ---------------------------------------------------------------------------
      45              : void
      46        43644 : MSDevice_GLOSA::insertOptions(OptionsCont& oc) {
      47        43644 :     oc.addOptionSubTopic("GLOSA Device");
      48        87288 :     insertDefaultAssignmentOptions("glosa", "GLOSA Device", oc);
      49              : 
      50        43644 :     oc.doRegister("device.glosa.range", new Option_Float(100.0));
      51        87288 :     oc.addDescription("device.glosa.range", "GLOSA Device", TL("The communication range to the traffic light"));
      52              : 
      53        43644 :     oc.doRegister("device.glosa.max-speedfactor", new Option_Float(1.1));
      54        87288 :     oc.addDescription("device.glosa.max-speedfactor", "GLOSA Device", TL("The maximum speed factor when approaching a green light"));
      55              : 
      56        43644 :     oc.doRegister("device.glosa.min-speed", new Option_Float(5.0));
      57        87288 :     oc.addDescription("device.glosa.min-speed", "GLOSA Device", TL("Minimum speed when coasting towards a red light"));
      58              : 
      59        43644 :     oc.doRegister("device.glosa.add-switchtime", new Option_Float(0.0));
      60        87288 :     oc.addDescription("device.glosa.add-switchtime", "GLOSA Device", TL("Additional time the vehicle shall need to reach the intersection after the signal turns green"));
      61              : 
      62        43644 :     oc.doRegister("device.glosa.use-queue", new Option_Bool(false));
      63        87288 :     oc.addDescription("device.glosa.use-queue", "GLOSA Device", TL("Use queue in front of the tls for GLOSA calculation"));
      64              : 
      65        43644 :     oc.doRegister("device.glosa.override-safety", new Option_Bool(false));
      66        87288 :     oc.addDescription("device.glosa.override-safety", "GLOSA Device", TL("Override safety features - ignore the current light state, always follow GLOSA's predicted state"));
      67              : 
      68        43644 :     oc.doRegister("device.glosa.ignore-cfmodel", new Option_Bool(false));
      69        87288 :     oc.addDescription("device.glosa.ignore-cfmodel", "GLOSA Device", TL("Vehicles follow a perfect speed calculation - ignore speed calculations from the CF model if not safety critical"));
      70        43644 : }
      71              : 
      72              : 
      73              : void
      74      5104363 : MSDevice_GLOSA::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
      75      5104363 :     OptionsCont& oc = OptionsCont::getOptions();
      76      9484152 :     if (!MSGlobals::gUseMesoSim && equippedByDefaultAssignmentOptions(oc, "glosa", v, false)) {
      77           48 :         MSDevice_GLOSA* device = new MSDevice_GLOSA(v, "glosa_" + v.getID(),
      78              :                 v.getFloatParam("device.glosa.min-speed", true),
      79              :                 v.getFloatParam("device.glosa.range", true),
      80              :                 v.getFloatParam("device.glosa.max-speedfactor", true),
      81              :                 v.getFloatParam("device.glosa.add-switchtime", true),
      82           48 :                 v.getBoolParam("device.glosa.use-queue", true),
      83           48 :                 v.getBoolParam("device.glosa.override-safety", true),
      84          120 :                 v.getBoolParam("device.glosa.ignore-cfmodel", true));
      85           24 :         into.push_back(device);
      86              :     }
      87      5104363 : }
      88              : 
      89              : void
      90            0 : MSDevice_GLOSA::cleanup() {
      91              :     // cleaning up global state (if any)
      92            0 : }
      93              : 
      94              : // ---------------------------------------------------------------------------
      95              : // MSDevice_GLOSA-methods
      96              : // ---------------------------------------------------------------------------
      97           24 : MSDevice_GLOSA::MSDevice_GLOSA(SUMOVehicle& holder, const std::string& id, double minSpeed, double range, double maxSpeedFactor,
      98           24 :     double addSwitchTime, bool useQueue, bool overrideSafety, bool ignoreCFModel) :
      99              :     MSVehicleDevice(holder, id),
     100            0 :     myVeh(dynamic_cast<MSVehicle&>(holder)),
     101           24 :     myNextTLSLink(nullptr),
     102           24 :     myDistance(0),
     103           24 :     myMinSpeed(minSpeed),
     104           24 :     myRange(range),
     105           24 :     myMaxSpeedFactor(maxSpeedFactor),
     106           24 :     myAddSwitchTime(addSwitchTime),
     107           24 :     myOverrideSafety(overrideSafety),
     108           24 :     myIgnoreCFModel(ignoreCFModel),
     109           24 :     mySpeedAdviceActive(false),
     110           24 :     myUseQueue(useQueue)
     111              : 
     112              : {
     113           24 :     myOriginalSpeedFactor = myVeh.getChosenSpeedFactor();
     114           24 : }
     115              : 
     116              : 
     117           48 : MSDevice_GLOSA::~MSDevice_GLOSA() {
     118           48 : }
     119              : 
     120              : 
     121              : bool
     122          816 : MSDevice_GLOSA::notifyMove(SUMOTrafficObject& /*tObject*/, double oldPos,
     123              :                            double newPos, double /*newSpeed*/) {
     124          816 :     myDistance -= (newPos - oldPos);
     125          816 :     if (myNextTLSLink != nullptr && myDistance <= myRange) {
     126          412 :         const double vMax = myVeh.getLane()->getVehicleMaxSpeed(&myVeh);
     127          412 :         double timeToJunction = earliest_arrival(myDistance, vMax);
     128          412 :         int countOld = 0;
     129              :         // calculate "first" next phase, all coming Phases are calculated via "getTimeToNextSwitch"
     130          412 :         double timeToSwitch = getTimeToSwitch(myNextTLSLink, countOld);
     131          412 :         bool currentPhaseGreen = false;
     132          412 :         bool currentPhaseStop = false;
     133          412 :         bool solved = false;
     134              :         double nextSwitch = 0;
     135              :         nextSwitch = timeToSwitch;
     136              :         double QueueLength = 0;
     137              :         double greenTime = 0;
     138              :         double additionalJunctionTime = 0;
     139              :         // It takes factor [seconds/per meter queue] to dissolve the queue at drive off
     140              :         // experimental value from calibrated drone data
     141              :         double factor = 0.21;
     142              :         // Additional time (offset) for the leading vehicle to accelerate
     143              :         // experimental value from calibrated drone data
     144              :         double addition = 3;
     145              :         // Vehicles are often not able to drive with their desired speed vMax.
     146              :         // The further away from the junction, the more uncertain the arrival prediction becomes.
     147              :         // For a vehicle trying to reach the begin/end of a green phase, this is critical.
     148              :         // The vehicle should then rather opt for the next phase (trade travel time for traffic flow)
     149              :         int switchOffset = 0;
     150              : 
     151          412 :         if (myNextTLSLink->haveGreen()) { currentPhaseGreen = true; }
     152          348 :         else if (myNextTLSLink->haveRed() || myNextTLSLink->haveYellow()) { currentPhaseStop = true; }
     153              :         // else if any other phase, GLOSA does not interfere
     154              : #if defined DEBUG_GLOSA || defined DEBUG_QUEUE
     155              :         if (DEBUG_COND) {
     156              :             std::cout << SIMTIME << " veh=" << myVeh.getID() << " d=" << myDistance << " ttJ=" << timeToJunction << " ttS=" << timeToSwitch << "\n";
     157              :         }
     158              : #endif
     159          412 :         if (myUseQueue) {
     160              :             // Detect queue length at tls
     161            0 :             QueueLength = myNextTLSLink->getTLLogic()->getTLQueueLength(myNextTLSLink->getLaneBefore()->getID());
     162              : #ifdef DEBUG_QUEUE
     163              :             if (DEBUG_COND) {
     164              :                 std::cout << SIMTIME << " veh=" << myVeh.getID() << " Queuelength=" << QueueLength << "\n";
     165              :             }
     166              : #endif
     167            0 :             if (currentPhaseGreen) {
     168              :                 // how long has it already been green in this phase
     169            0 :                 greenTime = timeGreen(myNextTLSLink);
     170            0 :                 additionalJunctionTime = (QueueLength * factor + addition) - greenTime;
     171            0 :                 if ((additionalJunctionTime > 0) && (additionalJunctionTime < nextSwitch)) {
     172              :                     // do note use queue system if queue is to long
     173            0 :                     timeToJunction += additionalJunctionTime;
     174              :                 } else {
     175              :                     // important for case: "speed can be increased to arrive at tls while green"
     176              :                     additionalJunctionTime = 0;
     177              :                 }
     178              : #ifdef DEBUG_QUEUE
     179              :                 if (DEBUG_COND) {
     180              :                     std::cout << SIMTIME << " veh=" << myVeh.getID() << " Additonaljunctiontime=" << additionalJunctionTime << " Greentime=" << greenTime << " TimetoJunction(GreenQueue)=" << timeToJunction << "\n";
     181              :                 }
     182              : #endif
     183              :             }
     184              : 
     185            0 :             if (currentPhaseStop) {
     186            0 :                 nextSwitch += QueueLength * factor + addition;
     187              : #ifdef DEBUG_QUEUE
     188              :                 if (DEBUG_COND) {
     189              :                     std::cout << SIMTIME << " veh=" << myVeh.getID() << " Nextswitch(RedQueue)=" << nextSwitch << "\n";
     190              :                 }
     191              : #endif
     192              :             }
     193              :         }
     194              : 
     195              :         // Search for the next passable phase, maximum 10 Phases 
     196          472 :         for (int countwhile = 1; countwhile < 11; countwhile++) {
     197          472 :             if (currentPhaseGreen) {
     198              :                 // reset nextSwitch because the queue matters only for the current Phase
     199          124 :                 if (countwhile == 2 && myUseQueue) {
     200            0 :                     nextSwitch -= QueueLength * factor + addition;
     201              :                 }
     202          124 :                 if (mySpeedAdviceActive && myOriginalSpeedFactor > myVeh.getChosenSpeedFactor()) {
     203           12 :                     myVeh.setChosenSpeedFactor(myOriginalSpeedFactor);
     204           12 :                     mySpeedAdviceActive = false;
     205              :                 }
     206              : #ifdef DEBUG_GLOSA
     207              :                 if (DEBUG_COND) {
     208              :                     std::cout << SIMTIME << " veh=" << myVeh.getID() << " traffic light will be/is green" << "\n";
     209              :                     std::cout << SIMTIME << " veh=" << myVeh.getID()
     210              :                               << " timeToJunction=" << timeToJunction
     211              :                               << " nextSwitch=" << nextSwitch
     212              :                               << " myDistance=" << myDistance << "\n";
     213              :                 }
     214              : #endif
     215              :                 // if we arrive at the tls after it switched to red (else do nothing)
     216          124 :                 if (timeToJunction > nextSwitch) {
     217              :                     // if speed can be increased to arrive at tls while green (else look for next phases)
     218           44 :                     if (myMaxSpeedFactor > myOriginalSpeedFactor) {
     219           44 :                         const double vMax2 = vMax / myVeh.getChosenSpeedFactor() * myMaxSpeedFactor;
     220           44 :                         const double timetoJunction2 = earliest_arrival(myDistance, vMax2) + additionalJunctionTime;
     221              :                         // reaching the signal at yellow might be sufficient
     222           44 :                         const double yellowSlack = myVeh.getVehicleType().getParameter().getJMParam(SUMO_ATTR_JM_DRIVE_AFTER_YELLOW_TIME, 0);
     223              : #ifdef DEBUG_GLOSA
     224              :                         if (DEBUG_COND) {
     225              :                             std::cout << SIMTIME << " veh=" << myVeh.getID() 
     226              :                                       << " vMax2=" << vMax2 
     227              :                                       << " timetoJunction2=" << timetoJunction2 
     228              :                                       << " yellowSlack=" << yellowSlack << "\n";
     229              :                         }
     230              : #endif
     231              :                         // if increased speed is fast enough to arrive at tls while green (else look for next phases)
     232           44 :                         if (timetoJunction2 <= (nextSwitch + yellowSlack)) {
     233              :                             // increase speed factor up to a maximum if necessary and useful
     234              :                             // XXX could compute optimal speed factor here
     235           44 :                             myVeh.setChosenSpeedFactor(myMaxSpeedFactor);
     236           44 :                             mySpeedAdviceActive = true;
     237           44 :                             break; // solved
     238              :                         } else {
     239              :                             // speed can not be increased to arrive at tls while green
     240              :                             // next switch is calculated at the end of the for-loop
     241              :                         }
     242              :                     } else {
     243              :                         // speed increase is not enough to reach tls while green
     244              :                         // next switch is calculated at the end of the for-loop
     245              :                     }
     246              :                 } else {
     247              :                     // vehicle will arrive at tls while green
     248              :                     break; // solved
     249              :                 }
     250          348 :             } else if (currentPhaseStop) {
     251              :                 // tls is red at the moment
     252              : #ifdef DEBUG_GLOSA
     253              :                 if (DEBUG_COND) {
     254              :                     std::cout << SIMTIME << " veh=" << myVeh.getID() << " traffic light will be/is red" << "\n";
     255              :                     std::cout << SIMTIME << " veh=" << myVeh.getID()
     256              :                               << " timeToJunction=" << timeToJunction 
     257              :                               << " nextSwitch=" << nextSwitch
     258              :                               << " myDistance=" << myDistance << "\n";
     259              :                 }
     260              : #endif
     261          348 :                 if (countwhile == 2 && myUseQueue) {
     262              :                     // take queue into account if current green phase can not be passed and next red phase is used instead
     263            0 :                     nextSwitch += QueueLength * factor + addition;
     264              : #ifdef DEBUG_QUEUE
     265              :                     if (DEBUG_COND) {
     266              :                         std::cout << SIMTIME << " veh=" << myVeh.getID() << " nextSwitch(redadditon): " << nextSwitch << "\n";
     267              :                     }
     268              : #endif
     269              :                 }
     270          348 :                 adaptSpeed(myDistance, timeToJunction, nextSwitch + myAddSwitchTime, solved);
     271          348 :                 if (countwhile == 2 && myUseQueue) {
     272              :                     // reset queue because following phases should not calculate with current queue
     273            0 :                     nextSwitch -= QueueLength * factor + addition;
     274            0 :                     timeToJunction -= additionalJunctionTime;
     275              :                 }
     276          348 :                 if (solved) {
     277              :                     break; // solved
     278              :                 }
     279              :             }
     280              :             // calculate next Phase
     281           60 :             nextSwitch += getTimeToNextSwitch(myNextTLSLink, currentPhaseGreen, currentPhaseStop, countOld);
     282              :             // For vehicles far away from the junction we add an offset
     283           60 :             if (nextSwitch > 80.) {
     284            4 :                 nextSwitch += (double)switchOffset;
     285              :                 switchOffset = 6;
     286            4 :                 nextSwitch -= (double)switchOffset;
     287           56 :             } else if (nextSwitch > 60.) {
     288            0 :                 nextSwitch += (double)switchOffset;
     289              :                 switchOffset = 4;
     290            0 :                 nextSwitch -= (double)switchOffset;
     291           56 :             } else if (nextSwitch > 40.) {
     292           24 :                 nextSwitch += (double)switchOffset;
     293              :                 switchOffset = 3;
     294           24 :                 nextSwitch -= (double)switchOffset;
     295           32 :             } else if (nextSwitch > 20.) {
     296           32 :                 nextSwitch += (double)switchOffset;
     297              :                 switchOffset = 2;
     298           32 :                 nextSwitch -= (double)switchOffset;
     299              :             }
     300              :         }
     301              :     }
     302          816 :     return true; // keep the device
     303              : }
     304              : 
     305              : 
     306              : bool
     307           80 : MSDevice_GLOSA::notifyEnter(SUMOTrafficObject& /*veh*/, MSMoveReminder::Notification /*reason*/, const MSLane* /* enteredLane */) {
     308           80 :     const MSLink* prevLink = myNextTLSLink;
     309           80 :     myNextTLSLink = nullptr;
     310           80 :     const MSLane* lane = myVeh.getLane();
     311           80 :     if (myVeh.getDeparture() < SIMSTEP) {
     312              :         // no need to call at insertion
     313           56 :         myVeh.updateBestLanes();
     314              :     }
     315           80 :     const std::vector<MSLane*>& bestLaneConts = myVeh.getBestLanesContinuation(lane);
     316           80 :     double seen = lane->getLength() - myVeh.getPositionOnLane();
     317              :     int view = 1;
     318           80 :     std::vector<MSLink*>::const_iterator linkIt = MSLane::succLinkSec(myVeh, view, *lane, bestLaneConts);
     319          116 :     while (!lane->isLinkEnd(linkIt)) {
     320           68 :         if (!lane->getEdge().isInternal()) {
     321           36 :             if ((*linkIt)->isTLSControlled()) {
     322           32 :                 myNextTLSLink = *linkIt;
     323           32 :                 myDistance = seen;
     324           32 :                 break;
     325              :             }
     326              :         }
     327           36 :         lane = (*linkIt)->getViaLaneOrLane();
     328           36 :         if (!lane->getEdge().isInternal()) {
     329           32 :             view++;
     330              :         }
     331           36 :         seen += lane->getLength();
     332           36 :         linkIt = MSLane::succLinkSec(myVeh, view, *lane, bestLaneConts);
     333              :     }
     334           80 :     if (prevLink != nullptr && myNextTLSLink == nullptr) {
     335              :         // moved pass tls
     336           24 :         myVeh.setChosenSpeedFactor(myOriginalSpeedFactor);
     337           24 :         mySpeedAdviceActive = false;
     338           56 :     } else if (myNextTLSLink != nullptr && prevLink != myNextTLSLink) {
     339              :         // approaching new tls
     340              :         double tlsRange = 1e10;
     341           48 :         const std::string val = myNextTLSLink->getTLLogic()->getParameter("device.glosa.range", "1e10");
     342              :         try {
     343           24 :             tlsRange = StringUtils::toDouble(val);
     344            0 :         } catch (const NumberFormatException&) {
     345            0 :             WRITE_WARNINGF(TL("Invalid value '%' for parameter 'device.glosa.range' of traffic light '%'"),
     346              :                            val, myNextTLSLink->getTLLogic()->getID());
     347            0 :         }
     348           48 :         myRange = MIN2(myVeh.getFloatParam("device.glosa.range", true), tlsRange);
     349              :     }
     350              : 
     351              : #ifdef DEBUG_GLOSA
     352              :     if (DEBUG_COND) {
     353              :         std::cout << SIMTIME << " veh=" << myVeh.getID() << " enter=" << myVeh.getLane()->getID() << " tls=" << (myNextTLSLink == nullptr ? "NULL" : myNextTLSLink->getTLLogic()->getID()) << " dist=" << myDistance << "\n";
     354              :     }
     355              : #endif
     356           80 :     return true; // keep the device
     357              : }
     358              : 
     359              : 
     360              : double
     361          412 : MSDevice_GLOSA::getTimeToSwitch(const MSLink* tlsLink, int& countOld) {
     362              :     assert(tlsLink != nullptr);
     363              :     const MSTrafficLightLogic* const tl = tlsLink->getTLLogic();
     364              :     assert(tl != nullptr);
     365          412 :     const auto& phases = tl->getPhases();
     366          412 :     const int n = (int)phases.size();
     367          412 :     const int cur = tl->getCurrentPhaseIndex();
     368          412 :     SUMOTime result = tl->getNextSwitchTime() - SIMSTEP;
     369              : 
     370         1008 :     for (int i = 1; i < n; i++) {
     371         1008 :         const auto& phase = phases[(cur + i) % n];
     372         1008 :         const char ls = phase->getState()[tlsLink->getTLIndex()];
     373          944 :         if (((tlsLink->haveRed() || tlsLink->haveYellow()) && (ls == 'g' || ls == 'G'))
     374          660 :                 || (tlsLink->haveGreen() && ls != 'g' && ls != 'G')) {
     375          412 :             countOld = cur + i;
     376          412 :             break;
     377              :         }
     378          596 :         result += phase->duration;
     379              :     }
     380          412 :     return STEPS2TIME(result);
     381              : }
     382              : 
     383              : 
     384              : double
     385           60 : MSDevice_GLOSA::getTimeToNextSwitch(const MSLink* tlsLink, bool& currentPhaseGreen, bool& currentPhaseStop, int& countOld) {
     386              :     // get time till the tls switches to a phase the vehicle can reach
     387              :     assert(tlsLink != nullptr);
     388              :     const MSTrafficLightLogic* const tl = tlsLink->getTLLogic();
     389              :     assert(tl != nullptr);
     390           60 :     const auto& phases = tl->getPhases();
     391           60 :     const int n = (int)phases.size();
     392           60 :     const int cur = countOld;
     393              :     SUMOTime result = 0;
     394              : 
     395          120 :     for (int i = 0; i < n; i++) {
     396          120 :         const auto& phase = phases[(cur + i) % n];
     397          120 :         const char ls = phase->getState()[tlsLink->getTLIndex()];
     398          120 :         if (currentPhaseGreen && (ls == 'g' || ls == 'G')) {
     399            0 :             countOld = (cur + i)%n;
     400            0 :             break;
     401              :         }
     402          120 :         if (currentPhaseStop && (ls != 'g' && ls != 'G')) {
     403           60 :             countOld = (cur + i)%n;
     404           60 :             break;
     405              :         }
     406           60 :         result += phase->duration;
     407              :     }
     408           60 :     currentPhaseGreen = !currentPhaseGreen;
     409           60 :     currentPhaseStop = !currentPhaseStop;
     410           60 :     return STEPS2TIME(result);
     411              : }
     412              : 
     413              : double
     414            0 : MSDevice_GLOSA::timeGreen(const MSLink* tlsLink) {
     415              :     assert(tlsLink != nullptr);
     416              :     const MSTrafficLightLogic* const tl = tlsLink->getTLLogic();
     417              :     assert(tl != nullptr);
     418            0 :     const auto& phases = tl->getPhases();
     419            0 :     const int n = (int)phases.size();
     420            0 :     const int cur = tl->getCurrentPhaseIndex();
     421              :     // As there are multiple "microphases G" in one Greenphase this function only gives back
     422              :     // the already spent time in the current microphase
     423            0 :     SUMOTime result = tl->getSpentDuration();
     424              : 
     425            0 :     for (int i = 1; i < n; i++) {
     426            0 :         const auto& phase = phases[(cur - i) % n];
     427            0 :         const char ls = phase->getState()[tlsLink->getTLIndex()];
     428            0 :         if ( ls != 'g' && ls != 'G') {
     429              :             break;
     430              :         }
     431            0 :         result += phase->duration;
     432              :     }
     433            0 :     return STEPS2TIME(result);
     434              : }
     435              : 
     436              : 
     437              : 
     438              : double
     439          456 : MSDevice_GLOSA::earliest_arrival(double distance, double vMax) {
     440              :     // assume we keep acceleration until we hit maximum speed
     441          456 :     const double v = myVeh.getSpeed();
     442          456 :     const double a = myVeh.getCarFollowModel().getMaxAccel();
     443          456 :     const double accel_time = MIN2((vMax - v) / a, time_to_junction_at_continuous_accel(distance, v));
     444          456 :     const double remaining_dist = distance - distance_at_continuous_accel(v, accel_time);
     445          456 :     const double remaining_time = remaining_dist / vMax;
     446          456 :     return accel_time + remaining_time;
     447              : }
     448              : 
     449              : 
     450              : /*
     451              : double
     452              : MSDevice_GLOSA::latest_arrival(speed, distance, earliest) {
     453              :     // assume we keep current speed until within myRange and then decelerate to myMinSpeed
     454              :     speed = max(speed, GLOSA_MIN_SPEED)
     455              :     potential_decel_dist = min(distance, GLOSA_RANGE)
     456              :     decel_time = (speed - GLOSA_MIN_SPEED) / GLOSA_DECEL
     457              :     avg_decel_speed = (speed + GLOSA_MIN_SPEED) / 2.0
     458              :     decel_dist = decel_time * avg_decel_speed
     459              :     if decel_dist > potential_decel_dist:
     460              :         decel_dist = potential_decel_dist
     461              :         # XXX actually avg_decel_speed is higher in this case
     462              :         decel_time = decel_dist / avg_decel_speed
     463              :     slow_dist = potential_decel_dist - decel_dist
     464              :     fast_dist = distance - (decel_dist + slow_dist)
     465              :     result = fast_dist / speed + decel_time + slow_dist / GLOSA_MIN_SPEED
     466              :     if result < earliest:
     467              :         if (distance > 15):
     468              :             print("DEBUG: fixing latest arrival of %s to match earliest of %s" % (result, earliest))
     469              :         result = earliest
     470              :     return result
     471              :     return 0;
     472              : }
     473              : */
     474              : 
     475              : 
     476              : double
     477          456 : MSDevice_GLOSA::distance_at_continuous_accel(double speed, double time) {
     478              :     const double v = speed;
     479              :     const double t = time;
     480          456 :     const double a = myVeh.getCarFollowModel().getMaxAccel();
     481              :     // integrated area composed of a rectangle and a triangle
     482          456 :     return v * t + a * t * t / 2;
     483              : }
     484              : 
     485              : 
     486              : double
     487          456 : MSDevice_GLOSA::time_to_junction_at_continuous_accel(double d, double v) {
     488              :     // see distance_at_continuous_accel
     489              :     // t^2 + (2v/a)t - 2d/a = 0
     490          456 :     const double a = myVeh.getCarFollowModel().getMaxAccel();
     491          456 :     const double p_half = v / a;
     492          456 :     const double t = -p_half + sqrt(p_half * p_half + 2 * d / a);
     493          456 :     return t;
     494              : }
     495              : 
     496              : 
     497              : void
     498          348 : MSDevice_GLOSA::adaptSpeed(double distance, double /*timeToJunction*/, double timeToSwitch, bool &solved) {
     499              :     // ensure that myVehicle arrives at the
     500              :     // junction with maximum speed when it switches to green
     501              :     // car performs a slowDown at time z to speed x for duration y
     502              :     // there are two basic strategies
     503              :     // a) maximize z -> this saves road space but leads to low x and thus excessive braking
     504              :     // b) maximize x -> this saves fuel but wastes road
     505              :     // c) compromise: b) but only when distance to junction is below a threshold
     506              : 
     507          348 :     const double vMax = myVeh.getLane()->getSpeedLimit() * myOriginalSpeedFactor;
     508              : 
     509              :     // need to start/continue maneuver
     510              :     const double t = timeToSwitch;
     511          348 :     const double a = myVeh.getCarFollowModel().getMaxAccel();
     512          348 :     const double u = myMinSpeed;
     513              :     const double w = vMax;
     514              :     const double s = distance;
     515          348 :     const double v = myVeh.getSpeed();
     516              :     // x : target speed
     517              :     // y : slow down duration
     518              :     // s is composed of 1 trapezoid (decel), 1 rectangle (maintain), 1 trapezoid (accel)
     519              :     // s = (v^2-x^2)/2d + x*(t-y-(w-x)/a) + (w^2-x^2)/2a
     520              :     // y = (v-x)/d
     521              :     // solution for x curtesy of mathomatic.org
     522              : 
     523              :     // First, we calculate targetSpeed assuming we are driving that speed already (v=x)
     524              :     // If this results in targetSpeed < currentSpeed, then we need to decelerate (and vice versa)
     525          348 :     const double rootConst = a * a * t * t - 2.0 * a * w * t + 2 * a * s;
     526              :     double vConst = 0;
     527          348 :     if (rootConst >= 0) {
     528          324 :         vConst = sqrt(rootConst) - a * t + w;
     529              :     }
     530          348 :     double d = myVeh.getCarFollowModel().getMaxDecel();
     531          348 :     if (v < vConst) {
     532              :         d = a;
     533              :     }
     534              : 
     535              :     // Second, we calculate the correct targetSpeed, knowing if we need to accelerate or decelerate
     536              :     const double sign0 = -1; // quadratic formula solution x1 (choose solution with higher speed)
     537          348 :     const double root_argument = a * d * ((2.0 * d * (s - (w * t))) - ((v - w) * (v - w)) + (a * ((d * (t * t)) + (2.0 * (s - (t * v))))));
     538          348 :     if (root_argument < 0) {
     539              : #ifdef DEBUG_GLOSA
     540              :         WRITE_WARNINGF("GLOSA error 1 root_argument=% s=% t=% v=%", root_argument, s, t, v);
     541              : #endif
     542              :         // no reset of speedFactor because in this case, current speed can be kept        
     543           32 :         solved = true;
     544              : 
     545           32 :         return;
     546              :     }
     547          316 :     const double x = (((a * (v - (d * t))) + (d * w) - sign0 * sqrt(root_argument)) / (d + a));
     548          316 :     double y = (v - x) / d;
     549          316 :     if (v < x) {
     550          176 :         y = (x - v) / d;
     551              :     }
     552          316 :     if (s < (w * w - x * x) / 2.0 / a) {
     553              :         // end maneuver
     554            4 :         if (myIgnoreCFModel) {
     555              :             std::vector<std::pair<SUMOTime, double> > speedTimeLine;
     556            0 :             speedTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep(), myVeh.getSpeed()));
     557            0 :             speedTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep() + TIME2STEPS(w - x / a), vMax));
     558            0 :             myVeh.getInfluencer().setSpeedTimeLine(speedTimeLine);
     559            0 :         } else {
     560            4 :             myVeh.setChosenSpeedFactor(myOriginalSpeedFactor);
     561              :         }
     562            4 :         mySpeedAdviceActive = false;
     563            4 :         return;
     564              :     }
     565          312 :     if (!(x >= u && x <= w && y < t)) {
     566              : #ifdef DEBUG_GLOSA
     567              :         WRITE_WARNINGF("GLOSA error 2 x=% y=% s=% t=% v=%", x, y, s, t, v);
     568              : #endif
     569           56 :         if (x < u) {
     570              :  #ifdef DEBUG_GLOSA
     571              :             if (DEBUG_COND) {
     572              :                 std::cout << "veh=" << myVeh.getID() << " cant go slow enough" << "\n";
     573              :             }
     574              : #endif
     575              :             // no reset of speedFactor because in this case, current speed can be kept
     576              :             //myVeh.setChosenSpeedFactor(myOriginalSpeedFactor);
     577              :             //mySpeedAdviceActive = false;
     578            0 :             solved = true;
     579              :         }
     580              :         if (x > w) {
     581              : #ifdef DEBUG_GLOSA
     582              :             if (DEBUG_COND) {
     583              :                 std::cout << "veh=" << myVeh.getID() << " cant go fast enough" << "\n";
     584              :             }
     585              : #endif
     586              :         }
     587           56 :         return;
     588              :     }
     589              :     const double targetSpeed = x;
     590          256 :     const double duration = MAX2(y, TS);
     591          256 :     solved = true;
     592              : #ifdef DEBUG_GLOSA
     593              :     if (DEBUG_COND) {
     594              :         std::cout << "  targetSpeed=" << targetSpeed << " duration=" << duration << "\n";
     595              :     }
     596              : #endif
     597          256 :     if (myIgnoreCFModel) {
     598              :         std::vector<std::pair<SUMOTime, double> > speedTimeLine;
     599            0 :         speedTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep(), myVeh.getSpeed()));
     600            0 :         speedTimeLine.push_back(std::make_pair(MSNet::getInstance()->getCurrentTimeStep() + TIME2STEPS(duration), targetSpeed));
     601            0 :         myVeh.getInfluencer().setSpeedTimeLine(speedTimeLine);
     602            0 :     } else {
     603          256 :         myVeh.setChosenSpeedFactor(targetSpeed / myVeh.getLane()->getSpeedLimit());
     604              :     }
     605          256 :     mySpeedAdviceActive = true;
     606              : }
     607              : 
     608              : 
     609              : void
     610           24 : MSDevice_GLOSA::generateOutput(OutputDevice* /*tripinfoOut*/) const {
     611              :     /*
     612              :     if (tripinfoOut != nullptr) {
     613              :         tripinfoOut->openTag("glosa_device");
     614              :         tripinfoOut->closeTag();
     615              :     }
     616              :     */
     617           24 : }
     618              : 
     619              : std::string
     620            0 : MSDevice_GLOSA::getParameter(const std::string& key) const {
     621            0 :     if (key == "minSpeed") {
     622            0 :         return toString(myMinSpeed);
     623              :     }
     624            0 :     throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
     625              : }
     626              : 
     627              : 
     628              : void
     629            0 : MSDevice_GLOSA::setParameter(const std::string& key, const std::string& value) {
     630              :     double doubleValue;
     631              :     try {
     632            0 :         doubleValue = StringUtils::toDouble(value);
     633            0 :     } catch (NumberFormatException&) {
     634            0 :         throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'");
     635            0 :     }
     636            0 :     if (key == "minSpeed") {
     637            0 :         myMinSpeed = doubleValue;
     638              :     } else {
     639            0 :         throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
     640              :     }
     641            0 : }
     642              : 
     643              : 
     644              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1