diff --git a/src/inet/linklayer/ieee80211/mac/contract/IRateControl.h b/src/inet/linklayer/ieee80211/mac/contract/IRateControl.h index d940563ab9e..85a2258ea87 100644 --- a/src/inet/linklayer/ieee80211/mac/contract/IRateControl.h +++ b/src/inet/linklayer/ieee80211/mac/contract/IRateControl.h @@ -9,6 +9,7 @@ #define __INET_IRATECONTROL_H #include "inet/common/packet/Packet.h" +#include "inet/linklayer/common/MacAddress.h" #include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h" #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h" #include "inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211ControlInfo_m.h" @@ -25,7 +26,8 @@ class INET_API IRateControl public: virtual ~IRateControl() {} - virtual const physicallayer::IIeee80211Mode *getRate() = 0; + // Returns the rate to use for a unicast frame addressed to the given receiver. + virtual const physicallayer::IIeee80211Mode *getRate(const MacAddress& receiverAddress) = 0; virtual void frameTransmitted(Packet *frame, int retryCount, bool isSuccessful, bool isGivenUp) = 0; virtual void frameReceived(Packet *frame) = 0; }; diff --git a/src/inet/linklayer/ieee80211/mac/contract/IRateSelection.cc b/src/inet/linklayer/ieee80211/mac/contract/IRateSelection.cc index 8675a9f31d1..a5401b9bef8 100644 --- a/src/inet/linklayer/ieee80211/mac/contract/IRateSelection.cc +++ b/src/inet/linklayer/ieee80211/mac/contract/IRateSelection.cc @@ -7,11 +7,27 @@ #include "inet/linklayer/ieee80211/mac/contract/IRateSelection.h" +#include "inet/networklayer/common/L3AddressResolver.h" + namespace inet { namespace ieee80211 { +using namespace inet::physicallayer; + simsignal_t IRateSelection::datarateSelectedSignal = cComponent::registerSignal("datarateSelected"); +void IRateSelection::emitDatarateSelected(cComponent *emitter, const Ptr& header, const IIeee80211Mode *mode) +{ + double rate = mode->getDataMode()->getNetBitrate().get(); + auto dataHeader = dynamicPtrCast(header); + // naming the station sweeps the network, so skip it if nothing listens anyway + if (dataHeader != nullptr && !dataHeader->getReceiverAddress().isMulticast() && emitter->mayHaveListeners(datarateSelectedSignal)) { + cNamedObject details(L3AddressResolver().getHostNameWithMacAddress(dataHeader->getReceiverAddress()).c_str()); + emitter->emit(datarateSelectedSignal, rate, &details); + } + else + emitter->emit(datarateSelectedSignal, rate); +} + } // namespace ieee80211 } // namespace inet - diff --git a/src/inet/linklayer/ieee80211/mac/contract/IRateSelection.h b/src/inet/linklayer/ieee80211/mac/contract/IRateSelection.h index 716e45948e6..54e605a652d 100644 --- a/src/inet/linklayer/ieee80211/mac/contract/IRateSelection.h +++ b/src/inet/linklayer/ieee80211/mac/contract/IRateSelection.h @@ -26,6 +26,16 @@ class INET_API IRateSelection public: static simsignal_t datarateSelectedSignal; + // Emits datarateSelected on behalf of a coordination function. Unicast data frames are tagged + // with the name of the receiving station as a named details object, so that a + // demux(datarateSelected) result filter or a statistic visualizer can key a separate + // per-station series on it; this mirrors the condition under which ~RateSelection applies a + // per-receiver configured rate, so the details always name the station whose rate is + // reported. Control, management and group-addressed frames carry no per-station data rate and + // are emitted without details: the aggregate datarateSelected statistic still records them, a + // bar chart ignores them. + static void emitDatarateSelected(cComponent *emitter, const Ptr& header, const physicallayer::IIeee80211Mode *mode); + public: virtual ~IRateSelection() {} diff --git a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc index 0da41104ace..0fcbe490543 100644 --- a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc +++ b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc @@ -102,7 +102,7 @@ void Dcf::transmitControlResponseFrame(Packet *responsePacket, const PtrgetDataMode()->getNetBitrate().get(), responsePacket); + IRateSelection::emitDatarateSelected(this, responseHeader, responseMode); EV_DEBUG << "Datarate for " << responsePacket->getName() << " is set to " << responseMode->getDataMode()->getNetBitrate() << ".\n"; tx->transmitFrame(responsePacket, responseHeader, modeSet->getSifsTime(), this); delete responsePacket; @@ -166,7 +166,7 @@ void Dcf::transmitFrame(Packet *packet, simtime_t ifs) const auto& header = packet->peekAtFront(); auto mode = rateSelection->computeMode(packet, header); RateSelection::setFrameMode(packet, header, mode); - emit(IRateSelection::datarateSelectedSignal, mode->getDataMode()->getNetBitrate().get(), packet); + IRateSelection::emitDatarateSelected(this, header, mode); EV_DEBUG << "Datarate for " << packet->getName() << " is set to " << mode->getDataMode()->getNetBitrate() << ".\n"; auto pendingPacket = channelAccess->getInProgressFrames()->getPendingFrameFor(packet); auto duration = originatorProtectionMechanism->computeDurationField(packet, header, pendingPacket, pendingPacket == nullptr ? nullptr : pendingPacket->peekAtFront()); diff --git a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc index b3d11467343..c320b00cc88 100644 --- a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc +++ b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc @@ -668,7 +668,7 @@ void Hcf::transmitFrame(Packet *packet, simtime_t ifs) } auto mode = rateSelection->computeMode(packet, header, txop); setFrameMode(packet, header, mode); - emit(IRateSelection::datarateSelectedSignal, mode->getDataMode()->getNetBitrate().get(), packet); + IRateSelection::emitDatarateSelected(this, header, mode); EV_DEBUG << "Datarate for " << packet->getName() << " is set to " << mode->getDataMode()->getNetBitrate() << ".\n"; if (txop->getProtectionMechanism() == TxopProcedure::ProtectionMechanism::SINGLE_PROTECTION) { auto pendingPacket = channelOwner->getInProgressFrames()->getPendingFrameFor(packet); @@ -703,7 +703,7 @@ void Hcf::transmitControlResponseFrame(Packet *responsePacket, const PtrgetDataMode()->getNetBitrate().get(), responsePacket); + IRateSelection::emitDatarateSelected(this, responseHeader, responseMode); EV_DEBUG << "Datarate for " << responsePacket->getName() << " is set to " << responseMode->getDataMode()->getNetBitrate() << ".\n"; tx->transmitFrame(responsePacket, responseHeader, modeSet->getSifsTime(), this); delete responsePacket; diff --git a/src/inet/linklayer/ieee80211/mac/originator/TxopProcedure.cc b/src/inet/linklayer/ieee80211/mac/originator/TxopProcedure.cc index 0ec6898bd00..340270693a3 100644 --- a/src/inet/linklayer/ieee80211/mac/originator/TxopProcedure.cc +++ b/src/inet/linklayer/ieee80211/mac/originator/TxopProcedure.cc @@ -96,7 +96,7 @@ simtime_t TxopProcedure::getRemaining() const if (start == -1) throw cRuntimeError("Txop has not started yet"); auto now = simTime(); - return now > start + limit ? 0 : now - start; + return now > start + limit ? 0 : start + limit - now; } simtime_t TxopProcedure::getDuration() const diff --git a/src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.cc b/src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.cc index e96da78e693..9392a205cc7 100644 --- a/src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.cc +++ b/src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.cc @@ -19,18 +19,14 @@ void AarfRateControl::initialize(int stage) RateControlBase::initialize(stage); if (stage == INITSTAGE_LOCAL) { factor = par("increaseThresholdFactor"); - increaseThreshold = par("increaseThreshold"); maxIncreaseThreshold = par("maxIncreaseThreshold"); decreaseThreshold = par("decreaseThreshold"); interval = par("interval"); + WATCH_EXPR("numStations", (int)stations.size()); WATCH(factor); - WATCH(increaseThreshold); WATCH(maxIncreaseThreshold); WATCH(decreaseThreshold); WATCH(interval); - WATCH(timer); - WATCH(probing); - WATCH(numberOfConsSuccTransmissions); } else if (stage == INITSTAGE_LINK_LAYER) { } @@ -41,65 +37,81 @@ void AarfRateControl::handleMessage(cMessage *msg) throw cRuntimeError("This module doesn't handle self messages"); } + +AarfRateControl::State& AarfRateControl::stateFor(const MacAddress& receiverAddress) +{ + auto it = stations.find(receiverAddress); + if (it == stations.end()) { + State state; + state.address = receiverAddress; + state.mode = getInitialMode(); + state.increaseThreshold = par("increaseThreshold"); + state.timer = simTime(); // the interval starts when the station is first seen, not at t=0 + it = stations.insert({receiverAddress, state}).first; + emitDatarateChangedSignal(state.address, state.mode); + } + return it->second; +} + void AarfRateControl::frameTransmitted(Packet *frame, int retryCount, bool isSuccessful, bool isGivenUp) { - increaseRateIfTimerIsExpired(); - - if (!isSuccessful && probing) { // probing packet failed - numberOfConsSuccTransmissions = 0; - currentMode = decreaseRateIfPossible(currentMode); - emitDatarateChangedSignal(); - EV_DETAIL << "Decreased rate to " << *currentMode << endl; - multiplyIncreaseThreshold(factor); - resetTimer(); + State& state = stateFor(getReceiverAddress(frame)); + increaseRateIfTimerIsExpired(state); + + if (!isSuccessful && state.probing) { // probing packet failed + state.numberOfConsSuccTransmissions = 0; + state.mode = decreaseRateIfPossible(state.mode); + emitDatarateChangedSignal(state.address, state.mode); + EV_DETAIL << "Decreased rate to " << *state.mode << endl; + multiplyIncreaseThreshold(state, factor); + resetTimer(state); } else if (!isSuccessful && retryCount >= decreaseThreshold - 1) { // decreaseThreshold consecutive failed transmissions - numberOfConsSuccTransmissions = 0; - currentMode = decreaseRateIfPossible(currentMode); - emitDatarateChangedSignal(); - EV_DETAIL << "Decreased rate to " << *currentMode << endl; - resetIncreaseThreshdold(); - resetTimer(); + state.numberOfConsSuccTransmissions = 0; + state.mode = decreaseRateIfPossible(state.mode); + emitDatarateChangedSignal(state.address, state.mode); + EV_DETAIL << "Decreased rate to " << *state.mode << endl; + resetIncreaseThreshdold(state); + resetTimer(state); } else if (isSuccessful && retryCount == 0) - numberOfConsSuccTransmissions++; - - if (numberOfConsSuccTransmissions == increaseThreshold) { - numberOfConsSuccTransmissions = 0; - currentMode = increaseRateIfPossible(currentMode); - emitDatarateChangedSignal(); - EV_DETAIL << "Increased rate to " << *currentMode << endl; - resetTimer(); - probing = true; + state.numberOfConsSuccTransmissions++; + + if (state.numberOfConsSuccTransmissions == state.increaseThreshold) { + state.numberOfConsSuccTransmissions = 0; + state.mode = increaseRateIfPossible(state.mode); + emitDatarateChangedSignal(state.address, state.mode); + EV_DETAIL << "Increased rate to " << *state.mode << endl; + resetTimer(state); + state.probing = true; } else - probing = false; - + state.probing = false; } -void AarfRateControl::multiplyIncreaseThreshold(double factor) +void AarfRateControl::multiplyIncreaseThreshold(State& state, double factor) { - if (increaseThreshold * factor <= maxIncreaseThreshold) - increaseThreshold *= factor; + if (state.increaseThreshold * factor <= maxIncreaseThreshold) + state.increaseThreshold *= factor; } -void AarfRateControl::resetIncreaseThreshdold() +void AarfRateControl::resetIncreaseThreshdold(State& state) { - increaseThreshold = par("increaseThreshold"); + state.increaseThreshold = par("increaseThreshold"); } -void AarfRateControl::resetTimer() +void AarfRateControl::resetTimer(State& state) { - timer = simTime(); + state.timer = simTime(); } -void AarfRateControl::increaseRateIfTimerIsExpired() +void AarfRateControl::increaseRateIfTimerIsExpired(State& state) { - if (simTime() - timer >= interval) { - currentMode = increaseRateIfPossible(currentMode); - emitDatarateChangedSignal(); - EV_DETAIL << "Increased rate to " << *currentMode << endl; - resetTimer(); + if (simTime() - state.timer >= interval) { + state.mode = increaseRateIfPossible(state.mode); + emitDatarateChangedSignal(state.address, state.mode); + EV_DETAIL << "Increased rate to " << *state.mode << endl; + resetTimer(state); } } @@ -107,14 +119,14 @@ void AarfRateControl::frameReceived(Packet *frame) { } -const IIeee80211Mode *AarfRateControl::getRate() +const IIeee80211Mode *AarfRateControl::getRate(const MacAddress& receiverAddress) { Enter_Method("getRate"); - increaseRateIfTimerIsExpired(); - EV_INFO << "The current mode is " << currentMode << " the net bitrate is " << currentMode->getDataMode()->getNetBitrate() << std::endl; - return currentMode; + State& state = stateFor(receiverAddress); + increaseRateIfTimerIsExpired(state); + EV_INFO << "The current mode is " << state.mode << " the net bitrate is " << state.mode->getDataMode()->getNetBitrate() << std::endl; + return state.mode; } } /* namespace ieee80211 */ } /* namespace inet */ - diff --git a/src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.h b/src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.h index 96c35ea93a6..56bb114b9e4 100644 --- a/src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.h +++ b/src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.h @@ -19,28 +19,38 @@ namespace ieee80211 { class INET_API AarfRateControl : public RateControlBase { protected: - simtime_t timer = SIMTIME_ZERO; + // Per-receiver adaptive state (formerly single-instance module members). + struct State { + MacAddress address; // the receiver this state belongs to (for per-station rate attribution) + const physicallayer::IIeee80211Mode *mode = nullptr; + simtime_t timer = SIMTIME_ZERO; + bool probing = false; + int increaseThreshold = -1; + int numberOfConsSuccTransmissions = 0; + }; + std::map stations; + + // configuration, shared across stations simtime_t interval = SIMTIME_ZERO; - bool probing = false; - int increaseThreshold = -1; int maxIncreaseThreshold = -1; int decreaseThreshold = -1; double factor = -1; - int numberOfConsSuccTransmissions = 0; - protected: virtual int numInitStages() const override { return NUM_INIT_STAGES; } virtual void initialize(int stage) override; virtual void handleMessage(cMessage *msg) override; - virtual void multiplyIncreaseThreshold(double factor); - virtual void resetIncreaseThreshdold(); - virtual void resetTimer(); - virtual void increaseRateIfTimerIsExpired(); + virtual State& stateFor(const MacAddress& receiverAddress); + virtual void resetRateControl() override { stations.clear(); } + + virtual void multiplyIncreaseThreshold(State& state, double factor); + virtual void resetIncreaseThreshdold(State& state); + virtual void resetTimer(State& state); + virtual void increaseRateIfTimerIsExpired(State& state); public: - virtual const physicallayer::IIeee80211Mode *getRate() override; + virtual const physicallayer::IIeee80211Mode *getRate(const MacAddress& receiverAddress) override; virtual void frameTransmitted(Packet *frame, int retryCount, bool isSuccessful, bool isGivenUp) override; virtual void frameReceived(Packet *frame) override; }; diff --git a/src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.ned b/src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.ned index 01d48d21d7d..9ec36a21ebc 100644 --- a/src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.ned +++ b/src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.ned @@ -26,9 +26,10 @@ simple AarfRateControl extends SimpleModule like IRateControl // needed to increase the rate double increaseThresholdFactor = default(2); // When the transmission of the probing packet fails, increaseThreshold is multiplied by increaseThresholdFactor. int maxIncreaseThreshold = default(50); // Upper bound for increaseThreshold. - displayStringTextFormat = default("{currentMode}"); + displayStringTextFormat = default("{numStations} stations"); @display("i=block/cogwheel"); @signal[datarateChanged]; - @statistic[datarateChanged](title="datarate"; record=vector; interpolationmode=sample-hold); + @statistic[datarateChanged](title="datarate"; record=vector; interpolationmode=sample-hold); // aggregate rate over all stations (interleaved) + @statistic[dataratePerStation](title="data rate per station"; source=demux(datarateChanged); unit=bps; record=vector; interpolationmode=sample-hold); // one vector per receiver, demultiplexed by station } diff --git a/src/inet/linklayer/ieee80211/mac/ratecontrol/OnoeRateControl.cc b/src/inet/linklayer/ieee80211/mac/ratecontrol/OnoeRateControl.cc index 4fbc0110fa7..d1c9789c5ea 100644 --- a/src/inet/linklayer/ieee80211/mac/ratecontrol/OnoeRateControl.cc +++ b/src/inet/linklayer/ieee80211/mac/ratecontrol/OnoeRateControl.cc @@ -19,20 +19,30 @@ void OnoeRateControl::initialize(int stage) RateControlBase::initialize(stage); if (stage == INITSTAGE_LOCAL) { interval = par("interval"); - WATCH(timer); - WATCH(numOfRetries); - WATCH(credit); - WATCH(numOfSuccTransmissions); - WATCH(numOfGivenUpTransmissions); - WATCH(avgRetriesPerFrame); + WATCH_EXPR("numStations", (int)stations.size()); } } -void OnoeRateControl::resetStatisticalVariables() + +OnoeRateControl::State& OnoeRateControl::stateFor(const MacAddress& receiverAddress) +{ + auto it = stations.find(receiverAddress); + if (it == stations.end()) { + State state; + state.address = receiverAddress; + state.mode = getInitialMode(); + state.timer = simTime(); // the interval starts when the station is first seen, not at t=0 + it = stations.insert({receiverAddress, state}).first; + emitDatarateChangedSignal(state.address, state.mode); + } + return it->second; +} + +void OnoeRateControl::resetStatisticalVariables(State& state) { - numOfRetries = 0; - numOfSuccTransmissions = 0; - numOfGivenUpTransmissions = 0; + state.numOfRetries = 0; + state.numOfSuccTransmissions = 0; + state.numOfGivenUpTransmissions = 0; } void OnoeRateControl::handleMessage(cMessage *msg) @@ -42,20 +52,21 @@ void OnoeRateControl::handleMessage(cMessage *msg) void OnoeRateControl::frameTransmitted(Packet *frame, int retryCount, bool isSuccessful, bool isGivenUp) { - computeModeIfTimerIsExpired(); + State& state = stateFor(getReceiverAddress(frame)); + computeModeIfTimerIsExpired(state); if (isSuccessful) - numOfSuccTransmissions++; + state.numOfSuccTransmissions++; else if (isGivenUp) - numOfGivenUpTransmissions++; + state.numOfGivenUpTransmissions++; if (retryCount > 0) - numOfRetries++; + state.numOfRetries++; } -void OnoeRateControl::computeModeIfTimerIsExpired() +void OnoeRateControl::computeModeIfTimerIsExpired(State& state) { - if (simTime() - timer >= interval) { - computeMode(); - timer = simTime(); + if (simTime() - state.timer >= interval) { + computeMode(state); + state.timer = simTime(); } } @@ -63,42 +74,42 @@ void OnoeRateControl::frameReceived(Packet *frame) { } -void OnoeRateControl::computeMode() +void OnoeRateControl::computeMode(State& state) { - int numOfFrameTransmitted = numOfSuccTransmissions + numOfGivenUpTransmissions + numOfRetries; - avgRetriesPerFrame = double(numOfRetries) / (numOfSuccTransmissions + numOfGivenUpTransmissions); - - if (numOfSuccTransmissions > 0) { - if (numOfFrameTransmitted >= 10 && avgRetriesPerFrame > 1) { - currentMode = decreaseRateIfPossible(currentMode); - emitDatarateChangedSignal(); - EV_DETAIL << "Decreased rate to " << *currentMode << endl; - credit = 0; + int numOfFrameTransmitted = state.numOfSuccTransmissions + state.numOfGivenUpTransmissions + state.numOfRetries; + state.avgRetriesPerFrame = double(state.numOfRetries) / (state.numOfSuccTransmissions + state.numOfGivenUpTransmissions); + + if (state.numOfSuccTransmissions > 0) { + if (numOfFrameTransmitted >= 10 && state.avgRetriesPerFrame > 1) { + state.mode = decreaseRateIfPossible(state.mode); + emitDatarateChangedSignal(state.address, state.mode); + EV_DETAIL << "Decreased rate to " << *state.mode << endl; + state.credit = 0; } - else if (avgRetriesPerFrame >= 0.1) - credit--; + else if (state.avgRetriesPerFrame >= 0.1) + state.credit--; else - credit++; + state.credit++; - if (credit >= 10) { - currentMode = increaseRateIfPossible(currentMode); - emitDatarateChangedSignal(); - EV_DETAIL << "Increased rate to " << *currentMode << endl; - credit = 0; + if (state.credit >= 10) { + state.mode = increaseRateIfPossible(state.mode); + emitDatarateChangedSignal(state.address, state.mode); + EV_DETAIL << "Increased rate to " << *state.mode << endl; + state.credit = 0; } - resetStatisticalVariables(); + resetStatisticalVariables(state); } } -const IIeee80211Mode *OnoeRateControl::getRate() +const IIeee80211Mode *OnoeRateControl::getRate(const MacAddress& receiverAddress) { Enter_Method("getRate"); - computeModeIfTimerIsExpired(); - EV_INFO << "The current mode is " << currentMode << " the net bitrate is " << currentMode->getDataMode()->getNetBitrate() << std::endl; - return currentMode; + State& state = stateFor(receiverAddress); + computeModeIfTimerIsExpired(state); + EV_INFO << "The current mode is " << state.mode << " the net bitrate is " << state.mode->getDataMode()->getNetBitrate() << std::endl; + return state.mode; } } /* namespace ieee80211 */ } /* namespace inet */ - diff --git a/src/inet/linklayer/ieee80211/mac/ratecontrol/OnoeRateControl.h b/src/inet/linklayer/ieee80211/mac/ratecontrol/OnoeRateControl.h index 6e978d36851..d7558b002d4 100644 --- a/src/inet/linklayer/ieee80211/mac/ratecontrol/OnoeRateControl.h +++ b/src/inet/linklayer/ieee80211/mac/ratecontrol/OnoeRateControl.h @@ -19,27 +19,36 @@ namespace ieee80211 { class INET_API OnoeRateControl : public RateControlBase { protected: - simtime_t timer = SIMTIME_ZERO; + // Per-receiver adaptive state (formerly single-instance module members). + struct State { + MacAddress address; // the receiver this state belongs to (for per-station rate attribution) + const physicallayer::IIeee80211Mode *mode = nullptr; + simtime_t timer = SIMTIME_ZERO; + int numOfRetries = 0; + int numOfSuccTransmissions = 0; + int numOfGivenUpTransmissions = 0; + double avgRetriesPerFrame = 0; + int credit = 0; + }; + std::map stations; + + // configuration, shared across stations simtime_t interval = SIMTIME_ZERO; - int numOfRetries = 0; - int numOfSuccTransmissions = 0; - int numOfGivenUpTransmissions = 0; - - double avgRetriesPerFrame = 0; - int credit = 0; - protected: virtual int numInitStages() const override { return NUM_INIT_STAGES; } virtual void initialize(int stage) override; virtual void handleMessage(cMessage *msg) override; - virtual void computeMode(); - virtual void resetStatisticalVariables(); - virtual void computeModeIfTimerIsExpired(); + virtual State& stateFor(const MacAddress& receiverAddress); + virtual void resetRateControl() override { stations.clear(); } + + virtual void computeMode(State& state); + virtual void resetStatisticalVariables(State& state); + virtual void computeModeIfTimerIsExpired(State& state); public: - virtual const physicallayer::IIeee80211Mode *getRate() override; + virtual const physicallayer::IIeee80211Mode *getRate(const MacAddress& receiverAddress) override; virtual void frameTransmitted(Packet *frame, int retryCount, bool isSuccessful, bool isGivenUp) override; virtual void frameReceived(Packet *frame) override; }; diff --git a/src/inet/linklayer/ieee80211/mac/ratecontrol/OnoeRateControl.ned b/src/inet/linklayer/ieee80211/mac/ratecontrol/OnoeRateControl.ned index dcecaf3c474..966753c790f 100644 --- a/src/inet/linklayer/ieee80211/mac/ratecontrol/OnoeRateControl.ned +++ b/src/inet/linklayer/ieee80211/mac/ratecontrol/OnoeRateControl.ned @@ -20,9 +20,10 @@ simple OnoeRateControl extends SimpleModule like IRateControl @class(OnoeRateControl); double initialRate @unit(bps) = default(-1bps); // -1 means the fastest mandatory rate double interval @unit(s) = default(1s); - displayStringTextFormat = default("{currentMode}"); + displayStringTextFormat = default("{numStations} stations"); @display("i=block/cogwheel"); @signal[datarateChanged]; - @statistic[datarateChanged](title="datarate"; record=vector; interpolationmode=sample-hold); + @statistic[datarateChanged](title="datarate"; record=vector; interpolationmode=sample-hold); // aggregate rate over all stations (interleaved) + @statistic[dataratePerStation](title="data rate per station"; source=demux(datarateChanged); unit=bps; record=vector; interpolationmode=sample-hold); // one vector per receiver, demultiplexed by station } diff --git a/src/inet/linklayer/ieee80211/mac/ratecontrol/RateControlBase.cc b/src/inet/linklayer/ieee80211/mac/ratecontrol/RateControlBase.cc index 0101c90e18d..352479df59a 100644 --- a/src/inet/linklayer/ieee80211/mac/ratecontrol/RateControlBase.cc +++ b/src/inet/linklayer/ieee80211/mac/ratecontrol/RateControlBase.cc @@ -7,7 +7,9 @@ #include "inet/linklayer/ieee80211/mac/ratecontrol/RateControlBase.h" +#include "inet/common/ModuleAccess.h" #include "inet/common/Simsignals.h" +#include "inet/networklayer/common/L3AddressResolver.h" namespace inet { namespace ieee80211 { @@ -19,9 +21,6 @@ simsignal_t RateControlBase::datarateChangedSignal = cComponent::registerSignal( void RateControlBase::initialize(int stage) { ModeSetListener::initialize(stage); - - if (stage == INITSTAGE_LOCAL) - WATCH_EXPR("currentMode", currentMode ? currentMode->getName() : "none"); } const IIeee80211Mode *RateControlBase::increaseRateIfPossible(const IIeee80211Mode *currentMode) @@ -36,10 +35,30 @@ const IIeee80211Mode *RateControlBase::decreaseRateIfPossible(const IIeee80211Mo return newMode == nullptr ? currentMode : newMode; } -void RateControlBase::emitDatarateChangedSignal() +MacAddress RateControlBase::getReceiverAddress(Packet *frame) const +{ + const auto& header = frame->peekAtFront(); + return header->getReceiverAddress(); +} + +const IIeee80211Mode *RateControlBase::getInitialMode() { - bps rate = currentMode->getDataMode()->getNetBitrate(); - emit(datarateChangedSignal, rate.get()); + double initialRate = par("initialRate"); + return initialRate == -1 ? modeSet->getFastestMandatoryMode() : modeSet->getMode(bps(initialRate)); +} + +void RateControlBase::emitDatarateChangedSignal(const MacAddress& receiver, const IIeee80211Mode *mode) +{ + bps rate = mode->getDataMode()->getNetBitrate(); + // Emit once, tagging the value with the receiver as a named details object. The aggregate + // datarateChanged statistic ignores the details (so it is unchanged), while a demux(datarateChanged) + // statistic uses the details name to record a separate data-rate vector per station. + if (receiver.isBroadcast() || receiver.isMulticast()) + emit(datarateChangedSignal, rate.get()); + else { + cNamedObject details(L3AddressResolver().getHostNameWithMacAddress(receiver).c_str()); + emit(datarateChangedSignal, rate.get(), &details); + } } void RateControlBase::receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) @@ -48,9 +67,7 @@ void RateControlBase::receiveSignal(cComponent *source, simsignal_t signalID, cO if (signalID == modesetChangedSignal) { modeSet = check_and_cast(obj); - double initRate = par("initialRate"); - currentMode = initRate == -1 ? modeSet->getFastestMandatoryMode() : modeSet->getMode(bps(initRate)); - emitDatarateChangedSignal(); + resetRateControl(); } } diff --git a/src/inet/linklayer/ieee80211/mac/ratecontrol/RateControlBase.h b/src/inet/linklayer/ieee80211/mac/ratecontrol/RateControlBase.h index c0dff2f6ac2..2a61b5028fa 100644 --- a/src/inet/linklayer/ieee80211/mac/ratecontrol/RateControlBase.h +++ b/src/inet/linklayer/ieee80211/mac/ratecontrol/RateControlBase.h @@ -8,6 +8,7 @@ #ifndef __INET_RATECONTROLBASE_H #define __INET_RATECONTROLBASE_H +#include "inet/linklayer/common/MacAddress.h" #include "inet/linklayer/ieee80211/mac/common/ModeSetListener.h" #include "inet/linklayer/ieee80211/mac/contract/IRateControl.h" @@ -19,15 +20,22 @@ class INET_API RateControlBase : public ModeSetListener, public IRateControl public: static simsignal_t datarateChangedSignal; - protected: - const physicallayer::IIeee80211Mode *currentMode = nullptr; - protected: virtual int numInitStages() const override { return NUM_INIT_STAGES; } virtual void initialize(int stage) override; virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) override; - virtual void emitDatarateChangedSignal(); + // The receiver MAC address of a transmitted (or received) frame, which keys the per-station state. + virtual MacAddress getReceiverAddress(Packet *frame) const; + // The mode a newly seen station starts from: the initialRate parameter, or the fastest mandatory mode. + virtual const physicallayer::IIeee80211Mode *getInitialMode(); + // Emits datarateChanged with the receiver as a named details object, so a demux(datarateChanged) + // result filter can record a separate data-rate vector per station. The aggregate datarateChanged + // statistic ignores the details and is therefore unchanged. Group-addressed receivers are emitted + // without details (aggregate only). + virtual void emitDatarateChangedSignal(const MacAddress& receiver, const physicallayer::IIeee80211Mode *mode); + // Drops all per-station state; called by subclasses' override when the mode set changes. + virtual void resetRateControl() {} const physicallayer::IIeee80211Mode *increaseRateIfPossible(const physicallayer::IIeee80211Mode *currentMode); const physicallayer::IIeee80211Mode *decreaseRateIfPossible(const physicallayer::IIeee80211Mode *currentMode); diff --git a/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc b/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc index c8579ac4424..423eb138cb3 100644 --- a/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc +++ b/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc @@ -9,6 +9,7 @@ #include "inet/common/ModuleAccess.h" #include "inet/common/Simsignals.h" +#include "inet/networklayer/common/NetworkInterface.h" #include "inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Tag_m.h" namespace inet { @@ -40,6 +41,27 @@ void QosRateSelection::initialize(int stage) } } +void QosRateSelection::ensurePerReceiverModesResolved() +{ + if (perReceiverResolved) + return; + perReceiverResolved = true; + auto perReceiverBitrate = check_and_cast(par("dataFrameBitratePerReceiver").objectValue()); + for (auto& [path, value] : perReceiverBitrate->getFields()) { + auto module = findModuleByPath(path.c_str()); + if (module == nullptr) + throw cRuntimeError("dataFrameBitratePerReceiver: cannot resolve receiver interface module path '%s'", path.c_str()); + auto networkInterface = check_and_cast(module); + try { + auto mode = modeSet->getMode(bps(value.doubleValueInUnit("bps")), Hz(par("dataFrameBandwidth")), par("dataFrameNumSpatialStreams")); + perReceiverDataFrameMode[networkInterface->getMacAddress()] = mode; + } + catch (const cRuntimeError& e) { + throw cRuntimeError("dataFrameBitratePerReceiver: cannot use rate '%s' for receiver '%s': %s", value.str().c_str(), path.c_str(), e.what()); + } + } +} + const IIeee80211Mode *QosRateSelection::getMode(Packet *packet, const Ptr& header) { const auto& modeReqTag = packet->findTag(); @@ -119,6 +141,15 @@ const IIeee80211Mode *QosRateSelection::computeResponseBlockAckFrameMode(Packet const IIeee80211Mode *QosRateSelection::computeDataOrMgmtFrameMode(const Ptr& dataOrMgmtHeader) { + // Per-receiver override for originated unicast data frames (see dataFrameBitratePerReceiver). + // Wins over the interface-wide dataFrameMode / rate control; group-addressed and management + // frames are left to the existing rules below. + if (dynamicPtrCast(dataOrMgmtHeader) && !dataOrMgmtHeader->getReceiverAddress().isMulticast()) { + ensurePerReceiverModesResolved(); + auto it = perReceiverDataFrameMode.find(dataOrMgmtHeader->getReceiverAddress()); + if (it != perReceiverDataFrameMode.end()) + return it->second; + } if (dynamicPtrCast(dataOrMgmtHeader) && dataFrameMode) return dataFrameMode; if (dynamicPtrCast(dataOrMgmtHeader) && mgmtFrameMode) @@ -141,7 +172,7 @@ const IIeee80211Mode *QosRateSelection::computeDataOrMgmtFrameMode(const PtrgetRate(); + return dataOrMgmtRateControl->getRate(dataOrMgmtHeader->getReceiverAddress()); else return fastestMandatoryMode; } @@ -159,7 +190,7 @@ const IIeee80211Mode *QosRateSelection::computeDataOrMgmtFrameMode(const PtrgetRate(); + return dataOrMgmtRateControl->getRate(dataOrMgmtHeader->getReceiverAddress()); else return fastestMandatoryMode; } diff --git a/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.h b/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.h index 167acc72b63..86a58bb95eb 100644 --- a/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.h +++ b/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.h @@ -48,11 +48,20 @@ class INET_API QosRateSelection : public IQosRateSelection, public ModeSetListen const physicallayer::IIeee80211Mode *responseCtsFrameMode = nullptr; const physicallayer::IIeee80211Mode *responseBlockAckFrameMode = nullptr; + // per-receiver unicast data-frame modes, resolved lazily from dataFrameBitratePerReceiver + std::map perReceiverDataFrameMode; + bool perReceiverResolved = false; + protected: virtual int numInitStages() const override { return NUM_INIT_STAGES; } virtual void initialize(int stage) override; virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) override; + // Builds perReceiverDataFrameMode on first use. Deferred out of initialize() because peer + // MAC addresses are assigned during INITSTAGE_LINK_LAYER with undefined intra-stage module + // ordering; the first transmitted data frame occurs after all init stages, so this is race-free. + virtual void ensurePerReceiverModesResolved(); + virtual const physicallayer::IIeee80211Mode *getMode(Packet *packet, const Ptr& header); virtual const physicallayer::IIeee80211Mode *computeControlFrameMode(const Ptr& header, TxopProcedure *txopProcedure); virtual const physicallayer::IIeee80211Mode *computeDataOrMgmtFrameMode(const Ptr& dataOrMgmtHeader); @@ -60,6 +69,10 @@ class INET_API QosRateSelection : public IQosRateSelection, public ModeSetListen virtual bool isControlResponseFrame(const Ptr& header, TxopProcedure *txopProcedure); public: + // Per-receiver configured data-frame modes (resolved lazily from dataFrameBitratePerReceiver); + // used by the IEEE 802.11 rate visualizer to show configured rates before any traffic. + const std::map& getPerReceiverDataFrameModes() { ensurePerReceiverModesResolved(); return perReceiverDataFrameMode; } + // A control response frame is a control frame that is transmitted as a response to the reception of a frame a SIFS // time after the PPDU containing the frame that elicited the response, e.g. a CTS in response to an RTS // reception, an ACK in response to a DATA reception, a BlockAck in response to a BlockAckReq reception. In diff --git a/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.ned b/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.ned index b3b96441781..bd1638ff827 100644 --- a/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.ned +++ b/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.ned @@ -34,6 +34,14 @@ simple QosRateSelection extends SimpleModule double dataFrameBandwidth @unit(Hz) = default(nan Hz); // Unspecified by default int dataFrameNumSpatialStreams = default(-1); // Unspecified by default + // Per-receiver unicast data-frame rate. Keys are peer interface module paths (e.g. + // "host1.wlan[0]"), resolved to MAC addresses at run time; values are bitrates (bps). + // A unicast data frame whose receiver matches a key is transmitted at that rate; + // unmatched receivers fall back to dataFrameBitrate / the rate-control module. The + // bandwidth and spatial-stream count are taken from dataFrameBandwidth / + // dataFrameNumSpatialStreams. Empty by default (capability inert). + object dataFrameBitratePerReceiver = default({}); + double mgmtFrameBitrate @unit(bps) = default(-1bps); // Fastest double controlFrameBitrate @unit(bps) = default(-1bps); @display("i=block/cogwheel"); diff --git a/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.cc b/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.cc index 726b52c3f09..e2ef6ab06c0 100644 --- a/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.cc +++ b/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.cc @@ -60,6 +60,27 @@ void RateSelection::initialize(int stage) } } +void RateSelection::ensurePerReceiverModesResolved() +{ + if (perReceiverResolved) + return; + perReceiverResolved = true; + auto perReceiverBitrate = check_and_cast(par("dataFrameBitratePerReceiver").objectValue()); + for (auto& [path, value] : perReceiverBitrate->getFields()) { + auto module = findModuleByPath(path.c_str()); + if (module == nullptr) + throw cRuntimeError("dataFrameBitratePerReceiver: cannot resolve receiver interface module path '%s'", path.c_str()); + auto networkInterface = check_and_cast(module); + try { + auto mode = modeSet->getMode(bps(value.doubleValueInUnit("bps")), Hz(par("dataFrameBandwidth")), par("dataFrameNumSpatialStreams")); + perReceiverDataFrameMode[networkInterface->getMacAddress()] = mode; + } + catch (const cRuntimeError& e) { + throw cRuntimeError("dataFrameBitratePerReceiver: cannot use rate '%s' for receiver '%s': %s", value.str().c_str(), path.c_str(), e.what()); + } + } +} + const IIeee80211Mode *RateSelection::getMode(Packet *packet, const Ptr& header) { const auto& modeReqTag = packet->findTag(); @@ -114,6 +135,15 @@ const IIeee80211Mode *RateSelection::computeResponseCtsFrameMode(Packet *packet, // const IIeee80211Mode *RateSelection::computeDataOrMgmtFrameMode(const Ptr& dataOrMgmtHeader) { + // Per-receiver override for originated unicast data frames (see dataFrameBitratePerReceiver). + // Wins over the interface-wide dataFrameMode / rate control; group-addressed and management + // frames are left to the existing rules below. + if (dynamicPtrCast(dataOrMgmtHeader) && !dataOrMgmtHeader->getReceiverAddress().isMulticast()) { + ensurePerReceiverModesResolved(); + auto it = perReceiverDataFrameMode.find(dataOrMgmtHeader->getReceiverAddress()); + if (it != perReceiverDataFrameMode.end()) + return it->second; + } if (dataOrMgmtHeader->getReceiverAddress().isMulticast() && multicastFrameMode) return multicastFrameMode; if (dynamicPtrCast(dataOrMgmtHeader) && dataFrameMode) @@ -121,7 +151,7 @@ const IIeee80211Mode *RateSelection::computeDataOrMgmtFrameMode(const Ptr(dataOrMgmtHeader) && mgmtFrameMode) return mgmtFrameMode; if (dataOrMgmtRateControl) - return dataOrMgmtRateControl->getRate(); + return dataOrMgmtRateControl->getRate(dataOrMgmtHeader->getReceiverAddress()); else return fastestMandatoryMode; } diff --git a/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.h b/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.h index 98d525812f6..2146b0b9960 100644 --- a/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.h +++ b/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.h @@ -48,16 +48,29 @@ class INET_API RateSelection : public IRateSelection, public SimpleModule, publi const physicallayer::IIeee80211Mode *responseAckFrameMode = nullptr; const physicallayer::IIeee80211Mode *responseCtsFrameMode = nullptr; + // per-receiver unicast data-frame modes, resolved lazily from dataFrameBitratePerReceiver + std::map perReceiverDataFrameMode; + bool perReceiverResolved = false; + protected: virtual int numInitStages() const override { return NUM_INIT_STAGES; } virtual void initialize(int stage) override; virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) override; + // Builds perReceiverDataFrameMode on first use. Deferred out of initialize() because peer + // MAC addresses are assigned during INITSTAGE_LINK_LAYER with undefined intra-stage module + // ordering; the first transmitted data frame occurs after all init stages, so this is race-free. + virtual void ensurePerReceiverModesResolved(); + virtual const physicallayer::IIeee80211Mode *getMode(Packet *packet, const Ptr& header); virtual const physicallayer::IIeee80211Mode *computeControlFrameMode(const Ptr& header); virtual const physicallayer::IIeee80211Mode *computeDataOrMgmtFrameMode(const Ptr& dataOrMgmtHeader); public: + // Per-receiver configured data-frame modes (resolved lazily from dataFrameBitratePerReceiver); + // used by the IEEE 802.11 rate visualizer to show configured rates before any traffic. + const std::map& getPerReceiverDataFrameModes() { ensurePerReceiverModesResolved(); return perReceiverDataFrameMode; } + static void setFrameMode(Packet *packet, const Ptr& header, const physicallayer::IIeee80211Mode *mode); // A control response frame is a control frame that is transmitted as a response to the reception of a frame a SIFS diff --git a/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.ned b/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.ned index b60bba95a51..99d92b535ec 100644 --- a/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.ned +++ b/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.ned @@ -29,6 +29,14 @@ simple RateSelection extends SimpleModule like IRateSelection double dataFrameBandwidth @unit(Hz) = default(nan Hz); // Unspecified by default int dataFrameNumSpatialStreams = default(-1); // Unspecified by default + // Per-receiver unicast data-frame rate. Keys are peer interface module paths (e.g. + // "host1.wlan[0]"), resolved to MAC addresses at run time; values are bitrates (bps). + // A unicast data frame whose receiver matches a key is transmitted at that rate; + // unmatched receivers fall back to dataFrameBitrate / the rate-control module. The + // bandwidth and spatial-stream count are taken from dataFrameBandwidth / + // dataFrameNumSpatialStreams. Empty by default (capability inert). + object dataFrameBitratePerReceiver = default({}); + double mgmtFrameBitrate @unit(bps) = default(-1bps); // Fastest double controlFrameBitrate @unit(bps) = default(-1bps); @display("i=block/cogwheel"); diff --git a/src/inet/networklayer/common/L3AddressResolver.cc b/src/inet/networklayer/common/L3AddressResolver.cc index e6f5efc506f..17e116ea2b5 100644 --- a/src/inet/networklayer/common/L3AddressResolver.cc +++ b/src/inet/networklayer/common/L3AddressResolver.cc @@ -574,5 +574,20 @@ cModule *L3AddressResolver::findHostWithMacAddress(const MacAddress& addr) return entry ? entry->getInterfaceTable()->getHostModule() : nullptr; } +std::string L3AddressResolver::getHostNameWithMacAddress(const MacAddress& addr) +{ + cModule *host = findHostWithMacAddress(addr); + if (host == nullptr) + return addr.str(); + // the path relative to the network, so that hosts of the same name in different + // subnetworks get different names; for a host directly under the network this is + // just its name + std::string name = host->getFullPath(); + std::string networkPrefix = std::string(host->getSimulation()->getSystemModule()->getFullName()) + "."; + if (name.compare(0, networkPrefix.length(), networkPrefix) == 0) + name.erase(0, networkPrefix.length()); + return name; +} + } // namespace inet diff --git a/src/inet/networklayer/common/L3AddressResolver.h b/src/inet/networklayer/common/L3AddressResolver.h index 57ba2756728..44d8b989733 100644 --- a/src/inet/networklayer/common/L3AddressResolver.h +++ b/src/inet/networklayer/common/L3AddressResolver.h @@ -205,6 +205,15 @@ class INET_API L3AddressResolver * Find the host with the specified MAC address. Returns nullptr if not found. */ virtual cModule *findHostWithMacAddress(const MacAddress& addr); + + /** + * Returns a name identifying the host with the specified MAC address: its path + * relative to the network, which is just the host name unless the host is nested + * in a subnetwork. Falls back to the MAC address string if no such host is found. + * Useful wherever a peer has to be named by the address a model knows it by, e.g. + * to label the per-peer series of a demultiplexed statistic. + */ + virtual std::string getHostNameWithMacAddress(const MacAddress& addr); //@} }; diff --git a/tests/module/AarfRateControlPerReceiver_1.test b/tests/module/AarfRateControlPerReceiver_1.test new file mode 100644 index 00000000000..51f1c1c1c7a --- /dev/null +++ b/tests/module/AarfRateControlPerReceiver_1.test @@ -0,0 +1,103 @@ +%description: +Tests that AarfRateControl adapts its rate separately for each receiver, so that +feedback from one peer does not move the rate used towards another. + +One source saturates two ad-hoc peers at once: nearSink 20 m away, farSink 1200 m +away on a link poor enough that frames to it are lost and retried. The rate towards +the near peer must therefore stay at the initial 54 Mbps, while the rate towards the +far peer falls well below it, and both peers must keep receiving. + +Measured here: 54 Mbps towards the near peer and 24.7 Mbps towards the far one, with +3653 and 1350 packets delivered. + +With one set of adaptive state per module (the behavior before rate control was keyed +on the receiver) the two peers share a single rate, which the far peer's retries drag +down to a blend of the two: 29.6 Mbps time-average, with the near peer served at that +blended rate instead of the 54 Mbps its own link supports, delivering 3080 packets to +the near peer and only 380 to the far one. + +%#-------------------------------------------------------------------------------------------------------------- +%file: test.ned +import inet.networklayer.configurator.ipv4.Ipv4NetworkConfigurator; +import inet.node.inet.AdhocHost; +import inet.physicallayer.wireless.ieee80211.packetlevel.Ieee80211ScalarRadioMedium; + +network AarfRateControlPerReceiverTest +{ + submodules: + configurator: Ipv4NetworkConfigurator; + radioMedium: Ieee80211ScalarRadioMedium; + source: AdhocHost; + nearSink: AdhocHost; + farSink: AdhocHost; +} + +%#-------------------------------------------------------------------------------------------------------------- +%inifile: omnetpp.ini +[General] +network = AarfRateControlPerReceiverTest +ned-path = .;../../../../src +sim-time-limit = 2s +cmdenv-express-mode = true +**.vector-recording = false + +*.radioMedium.sameTransmissionStartTimeCheck = "ignore" +*.radioMedium.backgroundNoise.power = -100dBm +*.*.ipv4.arp.typename = "GlobalArp" +*.configurator.addStaticRoutes = false + +# ad-hoc, so no association is needed and a distant peer simply has a worse link +*.*.wlan[*].opMode = "g(erp)" +*.*.wlan[*].mgmt.typename = "Ieee80211MgmtAdhoc" +*.*.wlan[*].agent.typename = "" +*.*.wlan[*].radio.transmitter.power = 50mW + +*.*.mobility.typename = "StationaryMobility" +**.mobility.initFromDisplayString = false +*.source.mobility.initialX = 0m +*.source.mobility.initialY = 0m +*.source.mobility.initialZ = 0m +*.nearSink.mobility.initialX = 20m +*.nearSink.mobility.initialY = 0m +*.nearSink.mobility.initialZ = 0m +*.farSink.mobility.initialX = 1200m +*.farSink.mobility.initialY = 0m +*.farSink.mobility.initialZ = 0m + +# the rate control under test, adapting from the same starting rate for every peer +*.source.wlan[*].mac.dcf.rateControl.typename = "AarfRateControl" +*.source.wlan[*].mac.dcf.rateControl.initialRate = 54Mbps +*.source.wlan[*].mac.dcf.rateControl.increaseThreshold = 20 +*.source.wlan[*].mac.dcf.rateControl.decreaseThreshold = 5 +*.source.wlan[*].mac.dcf.rateSelection.dataFrameBitrate = -1bps + +# the same saturating stream to each peer +*.source.numApps = 2 +*.source.app[0].typename = "UdpBasicApp" +*.source.app[0].destAddresses = "nearSink" +*.source.app[0].destPort = 5000 +*.source.app[1].typename = "UdpBasicApp" +*.source.app[1].destAddresses = "farSink" +*.source.app[1].destPort = 5001 +*.source.app[*].messageLength = 1000B +*.source.app[*].sendInterval = 0.5ms +*.nearSink.numApps = 1 +*.nearSink.app[0].typename = "UdpSink" +*.nearSink.app[0].localPort = 5000 +*.farSink.numApps = 1 +*.farSink.app[0].typename = "UdpSink" +*.farSink.app[0].localPort = 5001 + +# the per-station rates the verdict is computed from, as a scalar per peer +*.source.wlan[*].mac.dcf.rateControl.dataratePerStation.result-recording-modes = timeavg + +%#-------------------------------------------------------------------------------------------------------------- +%postrun-command: gawk '/(near|far)Sink:dataratePerStation:timeavg/ { match($3, /^([a-zA-Z]+)Sink:/, m); r[m[1]] = $4 } /(near|far)Sink\.app\[0\] packetReceived:count/ { match($2, /\.([a-zA-Z]+)Sink\./, m); c[m[1]] = $4 } END { printf "bothServed=%s\n", (c["near"] > 0 && c["far"] > 0) ? "YES" : "NO"; printf "nearRateUndragged=%s\n", (r["near"] > 50e6) ? "YES" : "NO"; printf "farRateReduced=%s\n", (r["far"] > 0 && r["far"] < 30e6) ? "YES" : "NO"; printf "values near=%.1fMbps far=%.1fMbps nearPk=%d farPk=%d\n", r["near"]/1e6, r["far"]/1e6, c["near"], c["far"] }' results/*.sca > verdict.out +%contains: verdict.out +bothServed=YES +nearRateUndragged=YES +farRateReduced=YES +%#-------------------------------------------------------------------------------------------------------------- +%postrun-command: grep "undisposed object:" test.out > test_undisposed.out || true +%not-contains: test_undisposed.out +undisposed object: