// preferences should be a member wns::scheduler::ConnectionID ProportionalFair::getNextConnection(SchedulerStatePtr schedulerState, std::priority_queue<UserPreference> preferences) { wns::scheduler::ConnectionID next = -1; while (!preferences.empty()) { int priority = schedulerState->currentState->getCurrentPriority(); const float preference = preferences.top().first; const UserID user = preferences.top().second; MESSAGE_SINGLE(NORMAL, logger, "Selected user="******"Selected connection with CID="<<next); return next; } preferences.pop(); } return next; }
// maybe we could get rid of the phase length void ProportionalFair::updatePastDataRates(std::map<UserID, float> bitsBeforeThisFrame, std::map<UserID, float> bitsAfterThisFrame, simTimeType phaseLength) { UserID user; float bitsThisFrame = 0.0; float pastDataRate = 0.0; float currentRate = 0.0; for (std::map<UserID, float>::const_iterator iter = bitsBeforeThisFrame.begin(); iter != bitsBeforeThisFrame.end(); ++iter) { user = iter->first; bitsThisFrame = bitsBeforeThisFrame[user] - bitsAfterThisFrame[user]; currentRate = bitsThisFrame / phaseLength; pastDataRate = pastDataRates[user]; if (pastDataRates.find(user) != pastDataRates.end()) { pastDataRates[user] = (1.0-historyWeight) * currentRate + historyWeight * pastDataRates[user]; } else { // new user pastDataRates[user] = currentRate; } MESSAGE_SINGLE(NORMAL, logger, "updatePastDataRates("<<user.getName()<<","<<phaseLength<<"s): pastDataRate: new= "<< pastDataRates[user]<<" bit/s, old= "<<pastDataRate<<" bit/s, currentRate= "<<currentRate<<" bit/s"); } }
wns::scheduler::PowerCapabilities Strategy::getPowerCapabilities(const UserID user) const { /* This method changes the state "schedulerState->powerCapabilities". This is good and ok if all users are the same (default of all systems), but if they are different (future), APC must call this function anytime. */ switch (schedulerState->powerControlType) { case PowerControlDLMaster: { schedulerState->powerCapabilities = colleagues.registry->getPowerCapabilities(); break; } case PowerControlULMaster: { // peer unknown. Assume peer=UT. if (!user.isValid()) { MESSAGE_SINGLE(NORMAL, logger, "getPowerCapabilities(NULL): asking registry..."); schedulerState->powerCapabilities = colleagues.registry->getPowerCapabilities(user); MESSAGE_SINGLE(NORMAL, logger, "getPowerCapabilities(NULL): nominal=" << schedulerState->powerCapabilities.nominalPerSubband); return schedulerState->powerCapabilities; } schedulerState->powerCapabilities = colleagues.registry->getPowerCapabilities(user); break; } case PowerControlULSlave: { if (schedulerState->defaultTxPower!=wns::Power()) { // don't use powerCapabilities but masterBurst instead assure(schedulerState->defaultTxPower!=wns::Power(),"undefined defaultTxPower"); schedulerState->powerCapabilities.nominalPerSubband = schedulerState->defaultTxPower; schedulerState->powerCapabilities.maxPerSubband = schedulerState->defaultTxPower; // limit not used for slave schedulerState->powerCapabilities.maxOverall = schedulerState->defaultTxPower * 1000.0; } // not given (by masterBurst) because there is an InputSchedulingMap (new method) else { assure(schedulerState->currentState->strategyInput->inputSchedulingMap != wns::scheduler::SchedulingMapPtr(), "need inputSchedulingMap with txPower information"); // the power settings in inputSchedulingMap can be overwritten // if APC strategy is changed to use nominal power. Therefore we need these values: schedulerState->powerCapabilities = colleagues.registry->getPowerCapabilities(); } break; } default: { throw wns::Exception("invalid powerControlType"); } } // switch(powerControlType) if (user.isValid()) { MESSAGE_SINGLE(NORMAL, logger, "getPowerCapabilities(" << user.getName() << "): nominal=" << schedulerState->powerCapabilities.nominalPerSubband); } else { MESSAGE_SINGLE(NORMAL, logger, "getPowerCapabilities(NULL): nominal=" << schedulerState->powerCapabilities.nominalPerSubband); } assure(schedulerState->powerCapabilities.nominalPerSubband != wns::Power(), "undefined power nominalPerSubband="<<schedulerState->powerCapabilities.nominalPerSubband); return schedulerState->powerCapabilities; }
std::priority_queue<ProportionalFair::UserPreference> ProportionalFair::calculateUserPreferences(UserSet activeUsers, bool txStrategy) const { std::map<UserID, ChannelQualityOnOneSubChannel> sinrs; // preference for every user in a priority queue which automatically sorts std::priority_queue<UserPreference> preferences; assure(preferences.size()==0, "preferences.size() must be 0"); for ( UserSet::const_iterator iter = activeUsers.begin(); iter != activeUsers.end(); ++iter) { UserID user = *iter; assure(user.isValid(), "No valid user"); //determines the users SINRs depending on the tx/Rx mode of the strategy if (txStrategy) { sinrs[user] = colleagues.registry->estimateTxSINRAt(user); } else { sinrs[user] = colleagues.registry->estimateRxSINROf(user); } // calculate PhyModeRate for available users wns::Ratio sinr(sinrs[user].carrier / sinrs[user].interference); wns::service::phy::phymode::PhyModeInterfacePtr phyMode = colleagues.registry->getPhyModeMapper()->getBestPhyMode(sinr); assure(phyMode->isValid(),"invalid PhyMode"); // calculate userRate, which is the maximum possible data rate // for one subChannel for the best PhyMode available here float phyModeRate = phyMode->getDataRate(); // rate [b/s] of one subChannel float referenceRate; float pastDataRate = 1.0; // get the past data rate for this user: // iterate over the global past data rates map of all users and // find the past data rate for this user // there is exactly one pastDataRate value per userID int weight = colleagues.registry->getTotalNumberOfUsers(user); for (std::map<UserID, float>::const_iterator iter = pastDataRates.begin(); iter != pastDataRates.end(); ++iter) { if (iter->first/*userID*/ == user) { float dataRate = iter->second; // a RN must get a better share of the bandwidth // here: proportional to its number of users: assure(weight>0, "numberOfUsers(" <<user.getName()<<")=" << weight); dataRate /= static_cast<float>(weight); // dataRate now has the meaning of a weight. pastDataRate = dataRate; } } // for all userIDs in pastDataRates if (pastDataRate < 0.01) pastDataRate = 0.01; // preference is achievable current user rate divided by a history // factor that takes the past throughput of this user into account assure(maxRateOfSubchannel>0.0, "unknown maxRateOfSubchannel"); // goal is either rate (true) or resource (false) fairness if (rateFairness == true) { referenceRate = maxRateOfSubchannel; } else { referenceRate = phyModeRate; } // maxRateOfSubchannel is constant, with range [0..1][bit/s] float resultMaxThroughput = referenceRate / maxRateOfSubchannel; float resultPropFair = referenceRate / pastDataRate; // can be any range if (scalingBetweenMaxTPandPFair <= 0.5) { // variate the preference for each user, so that they differ a little bit (1%) // and the automatic sorting does not always give the same order // (would be a problem for identical preference weights). resultMaxThroughput *= (1 + 0.01*(*preferenceVariationDistribution)()); } float UserPref = (1.0-scalingBetweenMaxTPandPFair) * resultMaxThroughput +scalingBetweenMaxTPandPFair * resultPropFair; MESSAGE_SINGLE(NORMAL, logger, "getPreference("<<user.getName()<<"): weight=" << weight << ", pastDataRate= "<<pastDataRate<<" bit/s, UserPreference= "<<UserPref<<" (resultMaxThroughput="<<resultMaxThroughput<<",resultProportionalFair="<<resultPropFair<<")"); // calculate preferences for users and order them preferences.push(UserPreference(UserPref, user)); } return preferences; }
DSAResult BestCapacity::getSubChannelWithDSA(RequestForResource& request, SchedulerStatePtr schedulerState, SchedulingMapPtr schedulingMap) { DSAResult dsaResult; UserID user = request.user; if (userInfoMap.find(user) == userInfoMap.end()) { userInfoMap.insert(UserInfoMap::value_type(user, UserInfo(schedulerState->currentState->strategyInput->fChannels))); } UserInfo& userInfo = userInfoMap.find(user)->second; int lastUsedSubChannel = userInfo.lastUsedSubChannel; int subChannel = lastUsedSubChannel; int maxSubChannel = schedulingMap->subChannels.size(); int maxTimeSlots = schedulerState->currentState->strategyInput->getNumberOfTimeSlots(); int lastUsedTimeSlot = userInfo.lastUsedTimeSlot; int timeSlot = lastUsedTimeSlot; assure(maxSubChannel==schedulerState->currentState->strategyInput->fChannels,"maxSubChannel="<<maxSubChannel<<"!=fChannels"); ChannelQualitiesOnAllSubBandsPtr channelQualitiesOnAllSubBands = userInfo.channelQualitiesOnAllSubBands; if (channelQualitiesOnAllSubBands==ChannelQualitiesOnAllSubBandsPtr()) { // empty assure(channelQualitiesOfAllUsers->knowsUser(user),"channelQualitiesOfAllUsers["<<user.getName()<<"] invalid"); // ^ or should we ask the registryProxy for CQI here? channelQualitiesOnAllSubBands = channelQualitiesOfAllUsers->find(user)->second; userInfo.channelQualitiesOnAllSubBands = channelQualitiesOnAllSubBands; } MESSAGE_SINGLE(NORMAL, logger, "getSubChannelWithDSA("<<request.toString()<<"): lastSC="<<lastUsedSubChannel); int spatialLayer=0; bool found = false; bool giveUp = false; if (!schedulerState->isTx && adjacentSubchannelsOnUplink && (lastUsedSubChannel!=DSAsubChannelNotFound) // already used subChannel ) { // UL SC-FDMA // adjacentSubchannelsOnUplink while(!found && !giveUp) { // try same old subChannel again: subChannel = lastUsedSubChannel; if (!userInfo.usedSubChannels[subChannel] && channelIsUsable(subChannel, timeSlot, request, schedulerState, schedulingMap)) { // PDU fits in found=true; break; } else { // mark unusable userInfo.usedSubChannels[subChannel] = true; } // TODO: consider timeSlots !!! // try +/-1 +/-2 and so on for (int tryThisSubChannelOffset=0; tryThisSubChannelOffset<maxSubChannel/2; tryThisSubChannelOffset++) { subChannel = (lastUsedSubChannel + tryThisSubChannelOffset*userInfo.toggleOffset); if ((subChannel>=0) && !userInfo.usedSubChannels[subChannel] && (subChannel<maxSubChannel) && channelIsUsable(subChannel, timeSlot, request, schedulerState, schedulingMap)) { // PDU fits in found=true; break; } else { // mark unusable userInfo.usedSubChannels[subChannel] = true; } subChannel = (lastUsedSubChannel - tryThisSubChannelOffset*userInfo.toggleOffset); if ((subChannel>=0) && !userInfo.usedSubChannels[subChannel] && (subChannel<maxSubChannel) && channelIsUsable(subChannel, timeSlot, request, schedulerState, schedulingMap)) { // PDU fits in found=true; break; } else { // mark unusable userInfo.usedSubChannels[subChannel] = true; } } // for offset +/- // the resulting DSA may not be contiguous in the case of another small-band user nearby } // while userInfo.toggleOffset *= -1; } else { // free search BetterChannelCapacity comparator; // from SchedulerTypes.hpp wns::service::phy::phymode::PhyModeMapperInterface* phyModeMapper = colleagues.registry->getPhyModeMapper(); double channelCapacity = 0.0; double remainingTimeOnthisChannel = 0.0; wns::Power nominalPower; // to get tx power, default or nominal // the same power will be used on all subchannels for comparing, so that // the best capacity depend only on the free time and datarate if (schedulerState->defaultTxPower!=wns::Power()) { // predefined, e.g. in slave mode nominalPower = schedulerState->defaultTxPower; } else { wns::scheduler::PowerCapabilities powerCapabilities = schedulerState->strategy->getPowerCapabilities(request.user); nominalPower = powerCapabilities.nominalPerSubband; } // O(N^2) operations: while(!found && !giveUp) { subChannel = DSAsubChannelNotFound; double bestChannelCapacity = 0.0; // find channel with best capacity of the remaining subChannels: for (int tryThisSubChannel=0; tryThisSubChannel<maxSubChannel; tryThisSubChannel++) { for (int tryThisTimeSlot=0; tryThisTimeSlot<maxTimeSlots; tryThisTimeSlot++) { // TODO: userInfo.usedSubChannels[tryThisSubChannel][tryThisTimeSlot] if (!userInfo.usedSubChannels[tryThisSubChannel]) { // could be free (at least not checked before) ChannelQualityOnOneSubChannel channelQuality = (*channelQualitiesOnAllSubBands)[tryThisSubChannel]; if (channelIsUsable(tryThisSubChannel, tryThisTimeSlot, request, schedulerState, schedulingMap)) { spatialLayer = getSpatialLayerForSubChannel(tryThisSubChannel, tryThisTimeSlot, request, schedulerState, schedulingMap); } else { continue; } remainingTimeOnthisChannel = schedulingMap->subChannels[tryThisSubChannel].temporalResources[tryThisTimeSlot]->physicalResources[spatialLayer].getFreeTime(); wns::Ratio sinr = nominalPower/(channelQuality.interference * channelQuality.pathloss.get_factor()); wns::SmartPtr<const wns::service::phy::phymode::PhyModeInterface> bestPhyMode = phyModeMapper->getBestPhyMode(sinr); channelCapacity = bestPhyMode->getDataRate() * remainingTimeOnthisChannel; if (comparator(channelCapacity, bestChannelCapacity)) { bestChannelCapacity = channelCapacity; subChannel = tryThisSubChannel; timeSlot = tryThisTimeSlot; } } // if unused } // forall tryThisTimeSlot } // forall tryThisSubChannel if (subChannel==DSAsubChannelNotFound) { // one complete round already done giveUp=true; break; } // best subChannel and spatialLayer found; now check if usable: if (channelIsUsable(subChannel, timeSlot, spatialLayer, request, schedulerState, schedulingMap)) { // PDU fits in found=true; break; } else { // mark unusable // TODO: userInfo.usedSubChannels[tryThisSubChannel][tryThisTimeSlot] userInfo.usedSubChannels[subChannel] = true; } } // while } // if free search or linear (SC-FDMA) if (giveUp) { MESSAGE_SINGLE(NORMAL, logger, "getSubChannelWithDSA(): no free subchannel"); return dsaResult; // empty with subChannel=DSAsubChannelNotFound } else { MESSAGE_SINGLE(NORMAL, logger, "getSubChannelWithDSA(): subChannel="<<subChannel); userInfo.lastUsedSubChannel = subChannel; dsaResult.subChannel = subChannel; dsaResult.spatialLayer = spatialLayer; return dsaResult; } return dsaResult; // empty with subChannel=DSAsubChannelNotFound }