Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/inet/linklayer/ieee80211/mac/contract/IRateControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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;
};
Expand Down
18 changes: 17 additions & 1 deletion src/inet/linklayer/ieee80211/mac/contract/IRateSelection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<const Ieee80211MacHeader>& header, const IIeee80211Mode *mode)
{
double rate = mode->getDataMode()->getNetBitrate().get<bps>();
auto dataHeader = dynamicPtrCast<const Ieee80211DataHeader>(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

10 changes: 10 additions & 0 deletions src/inet/linklayer/ieee80211/mac/contract/IRateSelection.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<const Ieee80211MacHeader>& header, const physicallayer::IIeee80211Mode *mode);

public:
virtual ~IRateSelection() {}

Expand Down
4 changes: 2 additions & 2 deletions src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ void Dcf::transmitControlResponseFrame(Packet *responsePacket, const Ptr<const I
else
throw cRuntimeError("Unknown received frame type");
RateSelection::setFrameMode(responsePacket, responseHeader, responseMode);
emit(IRateSelection::datarateSelectedSignal, responseMode->getDataMode()->getNetBitrate().get<bps>(), 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;
Expand Down Expand Up @@ -166,7 +166,7 @@ void Dcf::transmitFrame(Packet *packet, simtime_t ifs)
const auto& header = packet->peekAtFront<Ieee80211MacHeader>();
auto mode = rateSelection->computeMode(packet, header);
RateSelection::setFrameMode(packet, header, mode);
emit(IRateSelection::datarateSelectedSignal, mode->getDataMode()->getNetBitrate().get<bps>(), 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<Ieee80211DataOrMgmtHeader>());
Expand Down
4 changes: 2 additions & 2 deletions src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<bps>(), 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);
Expand Down Expand Up @@ -703,7 +703,7 @@ void Hcf::transmitControlResponseFrame(Packet *responsePacket, const Ptr<const I
else
throw cRuntimeError("Unknown received frame type");
setFrameMode(responsePacket, responseHeader, responseMode);
emit(IRateSelection::datarateSelectedSignal, responseMode->getDataMode()->getNetBitrate().get<bps>(), 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
110 changes: 61 additions & 49 deletions src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
}
Expand All @@ -41,80 +37,96 @@ 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);
}
Comment on lines +44 to +52

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 A newly seen peer's rate immediately jumps one step above the configured starting rate

The per-peer rate state is created with an unset timer (State state; at src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.cc:45), so the periodic "increase" deadline is already in the past the moment the peer appears, and the very first transmission to it is sent one rate step above the configured starting rate.
Impact: The configured starting rate is effectively ignored for every peer first used after the increase interval, and an extra spurious rate-change value is recorded for that peer.

Timer initialised to zero makes increaseRateIfTimerIsExpired fire on first use

State::timer defaults to SIMTIME_ZERO (src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.h:26). getRate() calls stateFor() and then increaseRateIfTimerIsExpired(state) (src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.cc:124-125), whose condition is simTime() - state.timer >= interval (default 50 ms). For any station whose state is created after t = 50 ms this is immediately true, so state.mode is bumped to the next faster mode and a second datarateChanged is emitted before the first frame is sent. Previously there was a single module-wide state created at t≈0, so this typically did not happen; with lazily created per-peer state it happens for essentially every peer. frameTransmitted() has the same path. Initialising state.timer = simTime() at creation preserves the intended "seeded from initialRate" semantics.

Suggested change
if (it == stations.end()) {
State state;
state.address = receiverAddress;
state.mode = getInitialMode();
state.increaseThreshold = par("increaseThreshold");
it = stations.insert({receiverAddress, state}).first;
emitDatarateChangedSignal(state.address, state.mode);
}
if (it == stations.end()) {
State state;
state.address = receiverAddress;
state.mode = getInitialMode();
state.increaseThreshold = par("increaseThreshold");
state.timer = simTime();
it = stations.insert({receiverAddress, state}).first;
emitDatarateChangedSignal(state.address, state.mode);
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a follow-up commit — confirmed, thanks.

Reproduced in examples/wireless/hiddennode with initialRate = 2Mbps and traffic starting at t = 1s: the per-station vector opened with two values at t = 1, 2 Mbps then 5.5 Mbps, and the first frame went out at 5.5 Mbps. With the timer seeded at creation it opens with a single 2 Mbps value.

OnoeRateControl has the same defect and is fixed in the same commit. It is milder there — computeMode() returns early while the station has no successful transmission yet — but it still divides by zero on the way in, leaving avgRetriesPerFrame at NaN.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 65474d9 (current head): the state is created with state.timer = simTime(), so the increase interval starts when the station is first seen rather than at t=0.

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);
}
}

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 */

30 changes: 20 additions & 10 deletions src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<MacAddress, State> 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;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Loading