Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cbef286
physicallayer: fix HT/VHT MCS definitions and guard interval timing
mgonzalezlopezudc Aug 29, 2026
6de1e5b
physicallayer: complete HT mode set guard interval catalog and lookup
mgonzalezlopezudc Aug 29, 2026
a978c0b
physicallayer: support atomic radio and transmitter mode switching
mgonzalezlopezudc Aug 29, 2026
2d90223
linklayer: configure guard interval in rate selection
mgonzalezlopezudc Aug 29, 2026
046347d
ieee80211: coordinate dynamic mode switches and enforce SSID bounds
mgonzalezlopezudc Aug 29, 2026
ae2f0d0
linklayer: rebuild configured fixed modes on dynamic mode-set changes
mgonzalezlopezudc Aug 29, 2026
3c56539
ieee80211: use legacy basic rates for group-addressed frames
mgonzalezlopezudc Sep 5, 2026
2433415
ieee80211: make radio mode-set transitions transactional
mgonzalezlopezudc Sep 5, 2026
a783eac
ieee80211: mark malformed response AIDs incorrect
mgonzalezlopezudc Sep 5, 2026
ff9be1c
ieee80211: commit mode-set changes before observer notification
mgonzalezlopezudc Sep 5, 2026
1a30494
ieee80211: publish listening changes after observer failures
mgonzalezlopezudc Sep 5, 2026
f07596a
feature(physicallayer): add HT Greenfield mode set and PHY transmissi…
mgonzalezlopezudc Aug 29, 2026
22dfa41
feature(linklayer): implement HT control-response rate selection and …
mgonzalezlopezudc Aug 29, 2026
e2c0eb1
fix(ieee80211): negotiate HT Greenfield capability and fall back to H…
mgonzalezlopezudc Aug 30, 2026
8965217
fix(linklayer): accept supported HT-mixed CTS responses in Greenfield…
mgonzalezlopezudc Aug 30, 2026
86dd5f4
test(linklayer): add unit tests for Greenfield receiver capability an…
mgonzalezlopezudc Aug 30, 2026
e3d2481
fix(physicallayer): fix transmission duration decomposition for Short…
mgonzalezlopezudc Aug 30, 2026
67eb646
tests(fingerprint): update baseline for wireless TXOP showcase
mgonzalezlopezudc Aug 30, 2026
da1dff3
physicallayer: preserve scalar signal phase durations
mgonzalezlopezudc Sep 5, 2026
658306a
ieee80211: restore the timed HT and VHT header phase
mgonzalezlopezudc Sep 5, 2026
4e2b967
ieee80211: bound mandatory multicast rates by the requested rate
mgonzalezlopezudc Sep 5, 2026
c922a5b
ieee80211: require unambiguous configured response modes
mgonzalezlopezudc Sep 5, 2026
2782671
ieee80211: document the packet PHY HT40 limitation
mgonzalezlopezudc Sep 5, 2026
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
7 changes: 7 additions & 0 deletions WHATSNEW
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ Notable backward incompatible changes are the following:
These changes are backward incompatible for C++ code that directly references
the old combined ICMP error indication or tag types.

7. 802.11n control-response rate selection

Ordinary ACK and Basic BlockAck responses in the n(mixed-2.4Ghz) profile now
use the mandatory non-HT fallback because BSSBasicRateSet is not modelled.
This can change airtime, timeout trajectories, throughput, and fingerprints
in existing simulations. An HT RTS continues to receive an HT-mixed CTS.

Notable backward compatible changes are the following:

1. IPv6 network configurator
Expand Down
37 changes: 37 additions & 0 deletions examples/wireless/lan80211/omnetpp-ht-greenfield.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[General]
network = Lan80211
abstract = true
cmdenv-express-mode = true

**.constraintAreaMinX = 0m
**.constraintAreaMinY = 0m
**.constraintAreaMinZ = 0m
**.constraintAreaMaxX = 600m
**.constraintAreaMaxY = 400m
**.constraintAreaMaxZ = 0m

**.arp.typename = "GlobalArp"
**.opMode = "n(greenfield-2.4Ghz)"
**.wlan[*].bitrate = 13Mbps

*.ap.wlan[*].address = "10:00:00:00:00:00"
*.host[*].**.mgmt.accessPointAddress = "10:00:00:00:00:00"

*.ap.mobility.typename = "StationaryMobility"
*.ap.mobility.initialX = 250m
*.ap.mobility.initialY = 200m
*.host[*].mobility.typename = "StationaryMobility"
*.host[*].mobility.initialY = 200m
*.host[0].mobility.initialX = 260m
*.host[1].mobility.initialX = 270m

[Config HtGreenfield]
description = "HT Greenfield mode exchanging ping traffic through an access point"
sim-time-limit = 2s
*.numHosts = 2
*.host[0].numApps = 0
*.host[1].numApps = 1
*.host[1].app[0].typename = "PingApp"
*.host[1].app[0].destAddr = "host[0]"
*.host[1].app[0].sendInterval = 100ms
*.host[1].app[0].printPing = true
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module ExtUpperIeee80211Interface extends ExtInterface like IWirelessInterface
{
parameters:
string energySourceModule = default("");
string opMode @enum("a","b","g(erp)","g(mixed)","n(mixed-2.4Ghz)","p","ac") = default("g(mixed)");
string opMode @enum("a","b","g(erp)","g(mixed)","n(mixed-2.4Ghz)","n(greenfield-2.4Ghz)","p","ac") = default("g(mixed)");
double bitrate @unit(bps) = default(-1bps);
**.opMode = this.opMode;
**.bitrate = this.bitrate;
Expand Down Expand Up @@ -92,4 +92,3 @@ module ExtUpperIeee80211Interface extends ExtInterface like IWirelessInterface

classifier.in <-- { @display("m=n"); } <-- tap.lowerLayerOut;
}

3 changes: 1 addition & 2 deletions src/inet/linklayer/ieee80211/Ieee80211Interface.ned
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ module Ieee80211Interface extends NetworkInterface like IWirelessInterface
parameters:
string interfaceTableModule;
string energySourceModule = default("");
string opMode @enum("a","b","g(erp)","g(mixed)","n(mixed-2.4Ghz)","p","ac") = default("g(mixed)");
string opMode @enum("a","b","g(erp)","g(mixed)","n(mixed-2.4Ghz)","n(greenfield-2.4Ghz)","p","ac") = default("g(mixed)");
string address @mutable = default("auto"); // MAC address as hex string (12 hex digits), or
// "auto". "auto" values will be replaced by
// a generated MAC address in init stage 0.
Expand Down Expand Up @@ -129,4 +129,3 @@ module Ieee80211Interface extends NetworkInterface like IWirelessInterface

classifier.in <-- { @display("m=n"); } <-- upperLayerIn;
}

60 changes: 46 additions & 14 deletions src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include "inet/linklayer/ieee80211/mac/Ieee80211Mac.h"

#include <tuple>

#include <algorithm>

#include "inet/common/INETUtils.h"
Expand Down Expand Up @@ -67,24 +69,12 @@ void Ieee80211Mac::initialize(int stage)
radioModule->subscribe(IRadio::receptionStateChangedSignal, this);
radioModule->subscribe(IRadio::transmissionStateChangedSignal, this);
radioModule->subscribe(IRadio::receivedSignalPartChangedSignal, this);
getContainingNicModule(this)->subscribe(modesetChangedSignal, this);
radio = check_and_cast<IRadio *>(radioModule);
ds = check_and_cast<IDs *>(getSubmodule("ds"));
rx = check_and_cast<IRx *>(getSubmodule("rx"));
tx = check_and_cast<ITx *>(getSubmodule("tx"));
int operationalHtSpatialStreamLimit = std::min(radio->getAntenna()->getNumAntennas(),
modeSet->getMaximumNumberOfSpatialStreams());
std::set<Hz> operationalChannelWidths;
if (modeSet->isHtOperationSupported()) {
const auto *transmitter = dynamic_cast<const Ieee80211Transmitter *>(radio->getTransmitter());
const auto *receiver = dynamic_cast<const Ieee80211Receiver *>(radio->getReceiver());
if (transmitter == nullptr || receiver == nullptr)
throw cRuntimeError("HT operation requires Ieee80211Transmitter and Ieee80211Receiver");
for (auto channelWidth : modeSet->getHtSupportedChannelWidths())
if (transmitter->isHtChannelWidthSupported(channelWidth) &&
receiver->isHtChannelWidthSupported(channelWidth))
operationalChannelWidths.insert(channelWidth);
}
mib->updateLocalHtCapabilities(modeSet, operationalChannelWidths, operationalHtSpatialStreamLimit);
updateLocalHtCapabilities();
emit(modesetChangedSignal, modeSet);
if (isUp())
initializeRadioMode();
Expand All @@ -97,6 +87,24 @@ void Ieee80211Mac::initialize(int stage)
}
}

void Ieee80211Mac::updateLocalHtCapabilities()
{
int operationalHtSpatialStreamLimit = std::min(radio->getAntenna()->getNumAntennas(),
modeSet->getMaximumNumberOfSpatialStreams());
std::set<Hz> operationalChannelWidths;
if (modeSet->isHtOperationSupported()) {
const auto *transmitter = dynamic_cast<const Ieee80211Transmitter *>(radio->getTransmitter());
const auto *receiver = dynamic_cast<const Ieee80211Receiver *>(radio->getReceiver());
if (transmitter == nullptr || receiver == nullptr)
throw cRuntimeError("HT operation requires Ieee80211Transmitter and Ieee80211Receiver");
for (auto channelWidth : modeSet->getHtSupportedChannelWidths())
if (transmitter->isHtChannelWidthSupported(channelWidth) &&
receiver->isHtChannelWidthSupported(channelWidth))
operationalChannelWidths.insert(channelWidth);
}
mib->updateLocalHtCapabilities(modeSet, operationalChannelWidths, operationalHtSpatialStreamLimit);
}

void Ieee80211Mac::initializeRadioMode()
{
const char *initialRadioMode = par("initialRadioMode");
Expand Down Expand Up @@ -346,6 +354,30 @@ void Ieee80211Mac::receiveSignal(cComponent *source, simsignal_t signalID, intva
}
}

void Ieee80211Mac::receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details)
{
Enter_Method("%s", cComponent::getSignalName(signalID));
if (signalID == modesetChangedSignal && obj != modeSet)
applyModeSet(check_and_cast<physicallayer::Ieee80211ModeSet *>(obj));
}

void Ieee80211Mac::applyModeSet(const physicallayer::Ieee80211ModeSet *newModeSet)
{
Enter_Method_Silent();
modeSet = const_cast<physicallayer::Ieee80211ModeSet *>(newModeSet);
updateLocalHtCapabilities();
}

std::function<void()> Ieee80211Mac::saveModeSetState()
{
Enter_Method_Silent();
auto restoreMib = mib->saveHtState();
return [this, state = std::make_tuple(modeSet), restoreMib]() mutable {
std::tie(modeSet) = std::move(state);
restoreMib();
};
}

void Ieee80211Mac::configureRadioMode(IRadio::RadioMode radioMode)
{
if (radio->getRadioMode() != radioMode) {
Expand Down
8 changes: 7 additions & 1 deletion src/inet/linklayer/ieee80211/mac/Ieee80211Mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "inet/linklayer/ieee80211/mac/coordinationfunction/Pcf.h"
#include "inet/linklayer/ieee80211/mib/Ieee80211Mib.h"
#include "inet/physicallayer/wireless/common/contract/packetlevel/IRadio.h"
#include "inet/physicallayer/wireless/ieee80211/contract/packetlevel/IIeee80211ModeSetListener.h"

namespace inet {
namespace ieee80211 {
Expand All @@ -35,10 +36,13 @@ class Ieee80211MacHeader;
* exact operation of the MAC depend on the plugged-in components (see IUpperMac,
* IRx, ITx, IContention and other interface classes).
*/
class INET_API Ieee80211Mac : public MacProtocolBase
class INET_API Ieee80211Mac : public MacProtocolBase, public physicallayer::IIeee80211ModeSetListener
{
public:
static simsignal_t frameTransmissionOutcomeSignal;
virtual const physicallayer::Ieee80211ModeSet *getModeSet() const override { return modeSet; }
virtual std::function<void()> saveModeSetState() override;
virtual void applyModeSet(const physicallayer::Ieee80211ModeSet *modeSet) override;

protected:
FcsMode fcsMode;
Expand All @@ -65,8 +69,10 @@ class INET_API Ieee80211Mac : public MacProtocolBase
virtual int numInitStages() const override { return NUM_INIT_STAGES; }
virtual void initialize(int) override;
virtual void initializeRadioMode();
void updateLocalHtCapabilities();

virtual void receiveSignal(cComponent *source, simsignal_t signalID, intval_t value, cObject *details) override;
virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) override;
using MacProtocolBase::receiveSignal;
virtual void configureRadioMode(physicallayer::IRadio::RadioMode radioMode);
virtual void configureNetworkInterface() override;
Expand Down
3 changes: 1 addition & 2 deletions src/inet/linklayer/ieee80211/mac/Ieee80211Mac.ned
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ module Ieee80211Mac extends MacProtocolBase like IIeee80211Mac
{
parameters:
string mibModule;
string modeSet @enum("a","b","g(erp)","g(mixed)","n(mixed-2.4Ghz)","p","ac") = default("g(mixed)");
string modeSet @enum("a","b","g(erp)","g(mixed)","n(mixed-2.4Ghz)","n(greenfield-2.4Ghz)","p","ac") = default("g(mixed)");
string fcsMode @enum("declared","computed") = default("declared");
string initialRadioMode @enum("off","sleep","receiver","transmitter","transceiver") = default("receiver");

Expand Down Expand Up @@ -135,4 +135,3 @@ module Ieee80211Mac extends MacProtocolBase like IIeee80211Mac
@display("p=250,200");
}
}

23 changes: 19 additions & 4 deletions src/inet/linklayer/ieee80211/mac/channelaccess/Dcaf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include "inet/linklayer/ieee80211/mac/channelaccess/Dcaf.h"

#include <tuple>

#include "inet/common/ModuleAccess.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h"
#include "inet/linklayer/ieee80211/mac/contract/IRx.h"
Expand Down Expand Up @@ -125,13 +127,26 @@ void Dcaf::expectedChannelAccess(simtime_t time)
void Dcaf::receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details)
{
Enter_Method("%s", cComponent::getSignalName(signalID));
if (signalID == modesetChangedSignal && obj != modeSet)
applyModeSet(check_and_cast<physicallayer::Ieee80211ModeSet *>(obj));
}

if (signalID == modesetChangedSignal) {
modeSet = check_and_cast<Ieee80211ModeSet *>(obj);
calculateTimingParameters();
}
void Dcaf::applyModeSet(const physicallayer::Ieee80211ModeSet *newModeSet)
{
Enter_Method_Silent();
modeSet = const_cast<physicallayer::Ieee80211ModeSet *>(newModeSet);
calculateTimingParameters();
}

std::function<void()> Dcaf::saveModeSetState()
{
Enter_Method_Silent();
return [this, state = std::make_tuple(modeSet, slotTime, sifs, ifs, eifs, cw, cwMin, cwMax)]() mutable {
std::tie(modeSet, slotTime, sifs, ifs, eifs, cw, cwMin, cwMax) = std::move(state);
};
}


} /* namespace ieee80211 */
} /* namespace inet */

5 changes: 4 additions & 1 deletion src/inet/linklayer/ieee80211/mac/channelaccess/Dcaf.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ namespace ieee80211 {

class INET_API Dcaf : public IChannelAccess, public IContention::ICallback, public IRecoveryProcedure::ICwCalculator, public ModeSetListener
{
public:
virtual std::function<void()> saveModeSetState() override;
virtual void applyModeSet(const physicallayer::Ieee80211ModeSet *modeSet) override;

protected:
physicallayer::Ieee80211ModeSet *modeSet = nullptr;
IContention *contention = nullptr;
IChannelAccess::ICallback *callback = nullptr;

Expand Down
23 changes: 19 additions & 4 deletions src/inet/linklayer/ieee80211/mac/channelaccess/Edcaf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include "inet/linklayer/ieee80211/mac/channelaccess/Edcaf.h"

#include <tuple>

#include "inet/common/ModuleAccess.h"
#include "inet/common/Simsignals.h"
#include "inet/networklayer/common/NetworkInterface.h"
Expand Down Expand Up @@ -189,13 +191,26 @@ int Edcaf::getCwMin(AccessCategory ac, int aCwMin)
void Edcaf::receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details)
{
Enter_Method("%s", cComponent::getSignalName(signalID));
if (signalID == modesetChangedSignal && obj != modeSet)
applyModeSet(check_and_cast<physicallayer::Ieee80211ModeSet *>(obj));
}

if (signalID == modesetChangedSignal) {
modeSet = check_and_cast<Ieee80211ModeSet *>(obj);
calculateTimingParameters();
}
void Edcaf::applyModeSet(const physicallayer::Ieee80211ModeSet *newModeSet)
{
Enter_Method_Silent();
modeSet = const_cast<physicallayer::Ieee80211ModeSet *>(newModeSet);
calculateTimingParameters();
}

std::function<void()> Edcaf::saveModeSetState()
{
Enter_Method_Silent();
return [this, state = std::make_tuple(modeSet, slotTime, sifs, ifs, eifs, cw, cwMin, cwMax)]() mutable {
std::tie(modeSet, slotTime, sifs, ifs, eifs, cw, cwMin, cwMax) = std::move(state);
};
}


} // namespace ieee80211
} // namespace inet

4 changes: 4 additions & 0 deletions src/inet/linklayer/ieee80211/mac/channelaccess/Edcaf.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ namespace ieee80211 {
*/
class INET_API Edcaf : public IChannelAccess, public IContention::ICallback, public IRecoveryProcedure::ICwCalculator, public ModeSetListener
{
public:
virtual std::function<void()> saveModeSetState() override;
virtual void applyModeSet(const physicallayer::Ieee80211ModeSet *modeSet) override;

protected:
IContention *contention = nullptr;
IChannelAccess::ICallback *callback = nullptr;
Expand Down
20 changes: 18 additions & 2 deletions src/inet/linklayer/ieee80211/mac/common/ModeSetListener.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include "inet/linklayer/ieee80211/mac/common/ModeSetListener.h"

#include <tuple>

#include "inet/common/ModuleAccess.h"
#include "inet/common/Simsignals.h"
#include "inet/networklayer/common/NetworkInterface.h"
Expand All @@ -23,11 +25,25 @@ void ModeSetListener::initialize(int stage)
void ModeSetListener::receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details)
{
Enter_Method("%s", cComponent::getSignalName(signalID));
if (signalID == modesetChangedSignal && obj != modeSet)
applyModeSet(check_and_cast<physicallayer::Ieee80211ModeSet *>(obj));
}

if (signalID == modesetChangedSignal)
modeSet = check_and_cast<physicallayer::Ieee80211ModeSet *>(obj);
void ModeSetListener::applyModeSet(const physicallayer::Ieee80211ModeSet *newModeSet)
{
Enter_Method_Silent();
modeSet = const_cast<physicallayer::Ieee80211ModeSet *>(newModeSet);
}

std::function<void()> ModeSetListener::saveModeSetState()
{
Enter_Method_Silent();
return [this, state = std::make_tuple(modeSet)]() mutable {
std::tie(modeSet) = std::move(state);
};
}


} /* namespace ieee80211 */
} /* namespace inet */

8 changes: 7 additions & 1 deletion src/inet/linklayer/ieee80211/mac/common/ModeSetListener.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@

#include "inet/common/SimpleModule.h"
#include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h"
#include "inet/physicallayer/wireless/ieee80211/contract/packetlevel/IIeee80211ModeSetListener.h"

namespace inet {
namespace ieee80211 {

class INET_API ModeSetListener : public SimpleModule, public cListener
class INET_API ModeSetListener : public SimpleModule, public cListener, public physicallayer::IIeee80211ModeSetListener
{
public:
virtual const physicallayer::Ieee80211ModeSet *getModeSet() const override { return modeSet; }
virtual std::function<void()> saveModeSetState() override;
virtual void applyModeSet(const physicallayer::Ieee80211ModeSet *modeSet) override;

protected:
physicallayer::Ieee80211ModeSet *modeSet = nullptr;

Expand Down
10 changes: 10 additions & 0 deletions src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ using namespace inet::physicallayer;

Define_Module(AarfRateControl);

std::function<void()> AarfRateControl::saveModeSetState()
{
Enter_Method_Silent();
auto restoreBase = ModeSetListener::saveModeSetState();
return [this, savedStations = stations, restoreBase]() mutable {
stations.swap(savedStations);
restoreBase();
};
}

void AarfRateControl::initialize(int stage)
{
RateControlBase::initialize(stage);
Expand Down
Loading
Loading