Skip to content

Commit 1acce62

Browse files
committed
ieee80211: add per-receiver configured unicast data-frame rates
dataFrameBitrate fixes one rate for the whole interface, so a scenario that needs different peers reached at different rates -- the usual setup for studying rate diversity -- had to fall back on an adaptive rate control and let the channel produce the spread indirectly, which is slow to set up and hard to hold steady. ~RateSelection and ~QosRateSelection now accept dataFrameBitratePerReceiver, a map from peer interface module path to bitrate. A unicast data frame whose receiver matches an entry is sent at that rate; everything else falls back to the existing rules. The parameter is empty by default, so the capability is inert unless configured. Resolution of the paths to MAC addresses is deferred to the first transmitted data frame rather than done in initialize(): peer MAC addresses are assigned during INITSTAGE_LINK_LAYER and the order of modules within a stage is undefined, so reading them from initialize() would be a race. By the time any data frame is sent, all init stages have completed. Unresolvable paths and rates the mode set does not support fail with an error naming the offending entry.
1 parent 76b7202 commit 1acce62

6 files changed

Lines changed: 103 additions & 0 deletions

File tree

src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "inet/common/ModuleAccess.h"
1111
#include "inet/common/Simsignals.h"
12+
#include "inet/networklayer/common/NetworkInterface.h"
1213
#include "inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Tag_m.h"
1314

1415
namespace inet {
@@ -40,6 +41,27 @@ void QosRateSelection::initialize(int stage)
4041
}
4142
}
4243

44+
void QosRateSelection::ensurePerReceiverModesResolved()
45+
{
46+
if (perReceiverResolved)
47+
return;
48+
perReceiverResolved = true;
49+
auto perReceiverBitrate = check_and_cast<cValueMap *>(par("dataFrameBitratePerReceiver").objectValue());
50+
for (auto& [path, value] : perReceiverBitrate->getFields()) {
51+
auto module = findModuleByPath(path.c_str());
52+
if (module == nullptr)
53+
throw cRuntimeError("dataFrameBitratePerReceiver: cannot resolve receiver interface module path '%s'", path.c_str());
54+
auto networkInterface = check_and_cast<NetworkInterface *>(module);
55+
try {
56+
auto mode = modeSet->getMode(bps(value.doubleValueInUnit("bps")), Hz(par("dataFrameBandwidth")), par("dataFrameNumSpatialStreams"));
57+
perReceiverDataFrameMode[networkInterface->getMacAddress()] = mode;
58+
}
59+
catch (const cRuntimeError& e) {
60+
throw cRuntimeError("dataFrameBitratePerReceiver: cannot use rate '%s' for receiver '%s': %s", value.str().c_str(), path.c_str(), e.what());
61+
}
62+
}
63+
}
64+
4365
const IIeee80211Mode *QosRateSelection::getMode(Packet *packet, const Ptr<const Ieee80211MacHeader>& header)
4466
{
4567
const auto& modeReqTag = packet->findTag<Ieee80211ModeReq>();
@@ -119,6 +141,15 @@ const IIeee80211Mode *QosRateSelection::computeResponseBlockAckFrameMode(Packet
119141

120142
const IIeee80211Mode *QosRateSelection::computeDataOrMgmtFrameMode(const Ptr<const Ieee80211DataOrMgmtHeader>& dataOrMgmtHeader)
121143
{
144+
// Per-receiver override for originated unicast data frames (see dataFrameBitratePerReceiver).
145+
// Wins over the interface-wide dataFrameMode / rate control; group-addressed and management
146+
// frames are left to the existing rules below.
147+
if (dynamicPtrCast<const Ieee80211DataHeader>(dataOrMgmtHeader) && !dataOrMgmtHeader->getReceiverAddress().isMulticast()) {
148+
ensurePerReceiverModesResolved();
149+
auto it = perReceiverDataFrameMode.find(dataOrMgmtHeader->getReceiverAddress());
150+
if (it != perReceiverDataFrameMode.end())
151+
return it->second;
152+
}
122153
if (dynamicPtrCast<const Ieee80211DataHeader>(dataOrMgmtHeader) && dataFrameMode)
123154
return dataFrameMode;
124155
if (dynamicPtrCast<const Ieee80211MgmtHeader>(dataOrMgmtHeader) && mgmtFrameMode)

src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,31 @@ class INET_API QosRateSelection : public IQosRateSelection, public ModeSetListen
4848
const physicallayer::IIeee80211Mode *responseCtsFrameMode = nullptr;
4949
const physicallayer::IIeee80211Mode *responseBlockAckFrameMode = nullptr;
5050

51+
// per-receiver unicast data-frame modes, resolved lazily from dataFrameBitratePerReceiver
52+
std::map<MacAddress, const physicallayer::IIeee80211Mode *> perReceiverDataFrameMode;
53+
bool perReceiverResolved = false;
54+
5155
protected:
5256
virtual int numInitStages() const override { return NUM_INIT_STAGES; }
5357
virtual void initialize(int stage) override;
5458
virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) override;
5559

60+
// Builds perReceiverDataFrameMode on first use. Deferred out of initialize() because peer
61+
// MAC addresses are assigned during INITSTAGE_LINK_LAYER with undefined intra-stage module
62+
// ordering; the first transmitted data frame occurs after all init stages, so this is race-free.
63+
virtual void ensurePerReceiverModesResolved();
64+
5665
virtual const physicallayer::IIeee80211Mode *getMode(Packet *packet, const Ptr<const Ieee80211MacHeader>& header);
5766
virtual const physicallayer::IIeee80211Mode *computeControlFrameMode(const Ptr<const Ieee80211MacHeader>& header, TxopProcedure *txopProcedure);
5867
virtual const physicallayer::IIeee80211Mode *computeDataOrMgmtFrameMode(const Ptr<const Ieee80211DataOrMgmtHeader>& dataOrMgmtHeader);
5968

6069
virtual bool isControlResponseFrame(const Ptr<const Ieee80211MacHeader>& header, TxopProcedure *txopProcedure);
6170

6271
public:
72+
// Per-receiver configured data-frame modes (resolved lazily from dataFrameBitratePerReceiver);
73+
// used by the IEEE 802.11 rate visualizer to show configured rates before any traffic.
74+
const std::map<MacAddress, const physicallayer::IIeee80211Mode *>& getPerReceiverDataFrameModes() { ensurePerReceiverModesResolved(); return perReceiverDataFrameMode; }
75+
6376
// A control response frame is a control frame that is transmitted as a response to the reception of a frame a SIFS
6477
// time after the PPDU containing the frame that elicited the response, e.g. a CTS in response to an RTS
6578
// reception, an ACK in response to a DATA reception, a BlockAck in response to a BlockAckReq reception. In

src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.ned

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ simple QosRateSelection extends SimpleModule
3434
double dataFrameBandwidth @unit(Hz) = default(nan Hz); // Unspecified by default
3535
int dataFrameNumSpatialStreams = default(-1); // Unspecified by default
3636

37+
// Per-receiver unicast data-frame rate. Keys are peer interface module paths (e.g.
38+
// "host1.wlan[0]"), resolved to MAC addresses at run time; values are bitrates (bps).
39+
// A unicast data frame whose receiver matches a key is transmitted at that rate;
40+
// unmatched receivers fall back to dataFrameBitrate / the rate-control module. The
41+
// bandwidth and spatial-stream count are taken from dataFrameBandwidth /
42+
// dataFrameNumSpatialStreams. Empty by default (capability inert).
43+
object dataFrameBitratePerReceiver = default({});
44+
3745
double mgmtFrameBitrate @unit(bps) = default(-1bps); // Fastest
3846
double controlFrameBitrate @unit(bps) = default(-1bps);
3947
@display("i=block/cogwheel");

src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,27 @@ void RateSelection::initialize(int stage)
6060
}
6161
}
6262

63+
void RateSelection::ensurePerReceiverModesResolved()
64+
{
65+
if (perReceiverResolved)
66+
return;
67+
perReceiverResolved = true;
68+
auto perReceiverBitrate = check_and_cast<cValueMap *>(par("dataFrameBitratePerReceiver").objectValue());
69+
for (auto& [path, value] : perReceiverBitrate->getFields()) {
70+
auto module = findModuleByPath(path.c_str());
71+
if (module == nullptr)
72+
throw cRuntimeError("dataFrameBitratePerReceiver: cannot resolve receiver interface module path '%s'", path.c_str());
73+
auto networkInterface = check_and_cast<NetworkInterface *>(module);
74+
try {
75+
auto mode = modeSet->getMode(bps(value.doubleValueInUnit("bps")), Hz(par("dataFrameBandwidth")), par("dataFrameNumSpatialStreams"));
76+
perReceiverDataFrameMode[networkInterface->getMacAddress()] = mode;
77+
}
78+
catch (const cRuntimeError& e) {
79+
throw cRuntimeError("dataFrameBitratePerReceiver: cannot use rate '%s' for receiver '%s': %s", value.str().c_str(), path.c_str(), e.what());
80+
}
81+
}
82+
}
83+
6384
const IIeee80211Mode *RateSelection::getMode(Packet *packet, const Ptr<const Ieee80211MacHeader>& header)
6485
{
6586
const auto& modeReqTag = packet->findTag<Ieee80211ModeReq>();
@@ -114,6 +135,15 @@ const IIeee80211Mode *RateSelection::computeResponseCtsFrameMode(Packet *packet,
114135
//
115136
const IIeee80211Mode *RateSelection::computeDataOrMgmtFrameMode(const Ptr<const Ieee80211DataOrMgmtHeader>& dataOrMgmtHeader)
116137
{
138+
// Per-receiver override for originated unicast data frames (see dataFrameBitratePerReceiver).
139+
// Wins over the interface-wide dataFrameMode / rate control; group-addressed and management
140+
// frames are left to the existing rules below.
141+
if (dynamicPtrCast<const Ieee80211DataHeader>(dataOrMgmtHeader) && !dataOrMgmtHeader->getReceiverAddress().isMulticast()) {
142+
ensurePerReceiverModesResolved();
143+
auto it = perReceiverDataFrameMode.find(dataOrMgmtHeader->getReceiverAddress());
144+
if (it != perReceiverDataFrameMode.end())
145+
return it->second;
146+
}
117147
if (dataOrMgmtHeader->getReceiverAddress().isMulticast() && multicastFrameMode)
118148
return multicastFrameMode;
119149
if (dynamicPtrCast<const Ieee80211DataHeader>(dataOrMgmtHeader) && dataFrameMode)

src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,29 @@ class INET_API RateSelection : public IRateSelection, public SimpleModule, publi
4848
const physicallayer::IIeee80211Mode *responseAckFrameMode = nullptr;
4949
const physicallayer::IIeee80211Mode *responseCtsFrameMode = nullptr;
5050

51+
// per-receiver unicast data-frame modes, resolved lazily from dataFrameBitratePerReceiver
52+
std::map<MacAddress, const physicallayer::IIeee80211Mode *> perReceiverDataFrameMode;
53+
bool perReceiverResolved = false;
54+
5155
protected:
5256
virtual int numInitStages() const override { return NUM_INIT_STAGES; }
5357
virtual void initialize(int stage) override;
5458
virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) override;
5559

60+
// Builds perReceiverDataFrameMode on first use. Deferred out of initialize() because peer
61+
// MAC addresses are assigned during INITSTAGE_LINK_LAYER with undefined intra-stage module
62+
// ordering; the first transmitted data frame occurs after all init stages, so this is race-free.
63+
virtual void ensurePerReceiverModesResolved();
64+
5665
virtual const physicallayer::IIeee80211Mode *getMode(Packet *packet, const Ptr<const Ieee80211MacHeader>& header);
5766
virtual const physicallayer::IIeee80211Mode *computeControlFrameMode(const Ptr<const Ieee80211MacHeader>& header);
5867
virtual const physicallayer::IIeee80211Mode *computeDataOrMgmtFrameMode(const Ptr<const Ieee80211DataOrMgmtHeader>& dataOrMgmtHeader);
5968

6069
public:
70+
// Per-receiver configured data-frame modes (resolved lazily from dataFrameBitratePerReceiver);
71+
// used by the IEEE 802.11 rate visualizer to show configured rates before any traffic.
72+
const std::map<MacAddress, const physicallayer::IIeee80211Mode *>& getPerReceiverDataFrameModes() { ensurePerReceiverModesResolved(); return perReceiverDataFrameMode; }
73+
6174
static void setFrameMode(Packet *packet, const Ptr<const Ieee80211MacHeader>& header, const physicallayer::IIeee80211Mode *mode);
6275

6376
// A control response frame is a control frame that is transmitted as a response to the reception of a frame a SIFS

src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.ned

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ simple RateSelection extends SimpleModule like IRateSelection
2929
double dataFrameBandwidth @unit(Hz) = default(nan Hz); // Unspecified by default
3030
int dataFrameNumSpatialStreams = default(-1); // Unspecified by default
3131

32+
// Per-receiver unicast data-frame rate. Keys are peer interface module paths (e.g.
33+
// "host1.wlan[0]"), resolved to MAC addresses at run time; values are bitrates (bps).
34+
// A unicast data frame whose receiver matches a key is transmitted at that rate;
35+
// unmatched receivers fall back to dataFrameBitrate / the rate-control module. The
36+
// bandwidth and spatial-stream count are taken from dataFrameBandwidth /
37+
// dataFrameNumSpatialStreams. Empty by default (capability inert).
38+
object dataFrameBitratePerReceiver = default({});
39+
3240
double mgmtFrameBitrate @unit(bps) = default(-1bps); // Fastest
3341
double controlFrameBitrate @unit(bps) = default(-1bps);
3442
@display("i=block/cogwheel");

0 commit comments

Comments
 (0)