From 688c765453c923a0083cfdd19c428dd4f65dd6f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Gonz=C3=A1lez=20L=C3=B3pez?= Date: Sat, 15 Aug 2026 14:09:41 +0200 Subject: [PATCH 1/4] feat(802.11n): add configurable HT Greenfield mode --- .../lan80211/omnetpp-ht-greenfield.ini | 38 +++++ .../ieee80211/ExtUpperIeee80211Interface.ned | 3 +- .../ieee80211/Ieee80211Interface.ned | 3 +- .../linklayer/ieee80211/mac/Ieee80211Mac.ned | 3 +- .../ieee80211/mode/Ieee80211HtMode.cc | 5 +- .../wireless/ieee80211/mode/Ieee80211HtMode.h | 3 +- .../ieee80211/mode/Ieee80211ModeSet.cc | 151 ++++++++++-------- .../ieee80211/packetlevel/Ieee80211Radio.ned | 3 +- .../packetlevel/Ieee80211Receiver.ned | 3 +- .../packetlevel/Ieee80211Transmitter.ned | 2 +- .../module/Ieee80211HtGreenfieldRuntime.test | 114 +++++++++++++ tests/unit/Ieee80211HtGreenfield_1.test | 46 ++++++ 12 files changed, 291 insertions(+), 83 deletions(-) create mode 100644 examples/wireless/lan80211/omnetpp-ht-greenfield.ini create mode 100644 tests/module/Ieee80211HtGreenfieldRuntime.test create mode 100644 tests/unit/Ieee80211HtGreenfield_1.test diff --git a/examples/wireless/lan80211/omnetpp-ht-greenfield.ini b/examples/wireless/lan80211/omnetpp-ht-greenfield.ini new file mode 100644 index 00000000000..9cc212679f4 --- /dev/null +++ b/examples/wireless/lan80211/omnetpp-ht-greenfield.ini @@ -0,0 +1,38 @@ +[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 +**.wlan[*].radio.transmitter.centerFrequency = 2.4GHz + +*.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 diff --git a/src/inet/emulation/linklayer/ieee80211/ExtUpperIeee80211Interface.ned b/src/inet/emulation/linklayer/ieee80211/ExtUpperIeee80211Interface.ned index 83b958c0f9f..57161c6354f 100644 --- a/src/inet/emulation/linklayer/ieee80211/ExtUpperIeee80211Interface.ned +++ b/src/inet/emulation/linklayer/ieee80211/ExtUpperIeee80211Interface.ned @@ -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; @@ -92,4 +92,3 @@ module ExtUpperIeee80211Interface extends ExtInterface like IWirelessInterface classifier.in <-- { @display("m=n"); } <-- tap.lowerLayerOut; } - diff --git a/src/inet/linklayer/ieee80211/Ieee80211Interface.ned b/src/inet/linklayer/ieee80211/Ieee80211Interface.ned index e32eaba59fc..3534c8d7c18 100644 --- a/src/inet/linklayer/ieee80211/Ieee80211Interface.ned +++ b/src/inet/linklayer/ieee80211/Ieee80211Interface.ned @@ -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. @@ -129,4 +129,3 @@ module Ieee80211Interface extends NetworkInterface like IWirelessInterface classifier.in <-- { @display("m=n"); } <-- upperLayerIn; } - diff --git a/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.ned b/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.ned index bcd161d3889..84c96742c33 100644 --- a/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.ned +++ b/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.ned @@ -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"); @@ -130,4 +130,3 @@ module Ieee80211Mac extends MacProtocolBase like IIeee80211Mac @display("p=250,200"); } } - diff --git a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.cc b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.cc index 319128cce20..628be02a656 100644 --- a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.cc +++ b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.cc @@ -326,7 +326,7 @@ Ieee80211HtCompliantModes::~Ieee80211HtCompliantModes() const Ieee80211HtMode *Ieee80211HtCompliantModes::getCompliantMode(const Ieee80211Htmcs *mcsMode, Ieee80211HtMode::BandMode centerFrequencyMode, Ieee80211HtPreambleMode::HighTroughputPreambleFormat preambleFormat, Ieee80211HtModeBase::GuardIntervalType guardIntervalType) { const char *name = ""; // TODO - auto htModeId = std::make_tuple(mcsMode->getBandwidth(), mcsMode->getMcsIndex(), guardIntervalType); + auto htModeId = std::make_tuple(mcsMode->getBandwidth(), mcsMode->getMcsIndex(), centerFrequencyMode, preambleFormat, guardIntervalType); auto mode = singleton.modeCache.find(htModeId); if (mode == singleton.modeCache.end()) { const Ieee80211OfdmModulation *modulation = nullptr; @@ -348,7 +348,7 @@ const Ieee80211HtMode *Ieee80211HtCompliantModes::getCompliantMode(const Ieee802 const Ieee80211HtDataMode *dataMode = new Ieee80211HtDataMode(mcsMode, mcsMode->getBandwidth(), guardIntervalType); const Ieee80211HtPreambleMode *preambleMode = new Ieee80211HtPreambleMode(htSignal, legacySignal, preambleFormat, dataMode->getNumberOfSpatialStreams()); const Ieee80211HtMode *htMode = new Ieee80211HtMode(name, preambleMode, dataMode, centerFrequencyMode); - singleton.modeCache.insert(std::pair, const Ieee80211HtMode *>(htModeId, htMode)); + singleton.modeCache.emplace(htModeId, htMode); return htMode; } return mode->second; @@ -544,4 +544,3 @@ const DI Ieee80211HtmcsTable::htMcs76BW40MHz([](){ return new Ie } /* namespace physicallayer */ } /* namespace inet */ - diff --git a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.h b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.h index e263107568e..a7295d35d36 100644 --- a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.h +++ b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.h @@ -460,7 +460,7 @@ class INET_API Ieee80211HtCompliantModes protected: static OPP_THREAD_LOCAL const Ieee80211HtCompliantModes singleton; - mutable std::map, const Ieee80211HtMode *> modeCache; + mutable std::map, const Ieee80211HtMode *> modeCache; public: Ieee80211HtCompliantModes(); @@ -473,4 +473,3 @@ class INET_API Ieee80211HtCompliantModes } /* namespace inet */ #endif - diff --git a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.cc b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.cc index 929d3e8cecc..85b2fb0d2ef 100644 --- a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.cc +++ b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.cc @@ -24,6 +24,87 @@ namespace physicallayer { Register_Abstract_Class(Ieee80211ModeSet); +#define HT_MODE_ENTRY(WIDTH, MCS, MANDATORY, FORMAT, GUARD_INTERVAL) \ + { MANDATORY, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs##MCS##BW##WIDTH##MHz, Ieee80211HtMode::BAND_2_4GHZ, FORMAT, GUARD_INTERVAL) }, +#define HT_MODE_ENTRIES_20(FORMAT) \ + HT_MODE_ENTRY(20, 0, true, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) \ + HT_MODE_ENTRY(20, 1, true, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) \ + HT_MODE_ENTRY(20, 2, true, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) \ + HT_MODE_ENTRY(20, 3, true, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) \ + HT_MODE_ENTRY(20, 4, true, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) \ + HT_MODE_ENTRY(20, 5, true, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) \ + HT_MODE_ENTRY(20, 6, true, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) \ + HT_MODE_ENTRY(20, 7, true, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) \ + HT_MODE_ENTRY(20, 8, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 9, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 10, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 11, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 12, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 13, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 14, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 15, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 16, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 17, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 18, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 19, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 20, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 21, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 22, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 23, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 24, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 25, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 26, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 27, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 28, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 29, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 30, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(20, 31, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) +#define HT_MODE_ENTRIES_40(FORMAT) \ + HT_MODE_ENTRY(40, 0, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 1, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 2, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 3, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 4, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 5, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 6, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 7, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 8, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 9, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 10, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 11, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 12, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 13, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 14, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 15, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 16, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 17, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 18, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 19, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 20, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 21, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 22, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 23, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 24, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 25, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 26, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 27, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 28, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 29, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 30, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) \ + HT_MODE_ENTRY(40, 31, false, FORMAT, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) + +static Ieee80211ModeSet createHtModeSet(const char *name, Ieee80211HtPreambleMode::HighTroughputPreambleFormat preambleFormat) +{ + return Ieee80211ModeSet(name, { // This table is not complete; it only contains 2.4GHz homogeneous spatial streams, all mandatory and optional modes + HT_MODE_ENTRIES_20(preambleFormat) + HT_MODE_ENTRIES_40(preambleFormat) + }); +} + +#undef HT_MODE_ENTRIES_40 +#undef HT_MODE_ENTRIES_20 +#undef HT_MODE_ENTRY + const DelayedInitializer> Ieee80211ModeSet::modeSets([]() { return new std::vector { Ieee80211ModeSet("a", { { true, &Ieee80211OfdmCompliantModes::ofdmMode6MbpsCS20MHz }, @@ -76,72 +157,9 @@ const DelayedInitializer> Ieee80211ModeSet::modeSe { false, &Ieee80211OfdmCompliantModes::ofdmMode24MbpsCS10MHz }, { false, &Ieee80211OfdmCompliantModes::ofdmMode27Mbps }, }), - Ieee80211ModeSet("n(mixed-2.4Ghz)", { // This table is not complete; it only contains 2.4GHz homogeneous spatial streams, all mandatory and optional modes - { true, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs0BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) }, - { true, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs1BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) }, - { true, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs2BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) }, - { true, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs3BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) }, - { true, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs4BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) }, - { true, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs5BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) }, - { true, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs6BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) }, - { true, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs7BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs8BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs9BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs10BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs11BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs12BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs13BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs14BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs15BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs16BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs17BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs18BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs19BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs20BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs21BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs22BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs23BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs24BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs25BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs26BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs27BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs28BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs29BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs30BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs31BW20MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs0BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs1BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs2BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs3BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs4BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs5BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs6BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs7BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs8BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs9BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs10BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs11BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs12BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs13BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs14BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs15BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs16BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs17BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs18BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs19BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs20BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs21BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs22BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs23BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs24BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs25BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs26BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs27BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs28BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs29BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs30BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) }, - { false, Ieee80211HtCompliantModes::getCompliantMode(&Ieee80211HtmcsTable::htMcs31BW40MHz, Ieee80211HtMode::BAND_2_4GHZ, Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211HtModeBase::HT_GUARD_INTERVAL_SHORT) } - }), + createHtModeSet("n(mixed-2.4Ghz)", Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED), + // IEEE Std 802.11-2024, 19.1.4, 19.3.9.5, 19.4.3: HT-greenfield is a distinct optional PPDU format with separate timing. + createHtModeSet("n(greenfield-2.4Ghz)", Ieee80211HtPreambleMode::HT_PREAMBLE_GREENFIELD), Ieee80211ModeSet("ac", { { true, Ieee80211VhtCompliantModes::getCompliantMode(&Ieee80211VhtmcsTable::vhtMcs0BW20MHzNss1, Ieee80211VhtMode::BAND_5GHZ, Ieee80211VhtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211VhtModeBase::HT_GUARD_INTERVAL_LONG) }, { true, Ieee80211VhtCompliantModes::getCompliantMode(&Ieee80211VhtmcsTable::vhtMcs1BW20MHzNss1, Ieee80211VhtMode::BAND_5GHZ, Ieee80211VhtPreambleMode::HT_PREAMBLE_MIXED, Ieee80211VhtModeBase::HT_GUARD_INTERVAL_LONG) }, @@ -626,4 +644,3 @@ const Ieee80211ModeSet *Ieee80211ModeSet::getModeSet(const char *mode) } // namespace physicallayer } // namespace inet - diff --git a/src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Radio.ned b/src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Radio.ned index c32061114d3..2258e4c82bc 100644 --- a/src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Radio.ned +++ b/src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Radio.ned @@ -25,7 +25,7 @@ import inet.physicallayer.wireless.common.base.packetlevel.FlatRadioBase; module Ieee80211Radio extends FlatRadioBase { parameters: - string opMode @enum("a", "b", "g(erp)", "g(mixed)", "n(mixed-2.4Ghz)", "p", "ac") = default("g(mixed)"); // Operation mode + string opMode @enum("a", "b", "g(erp)", "g(mixed)", "n(mixed-2.4Ghz)", "n(greenfield-2.4Ghz)", "p", "ac") = default("g(mixed)"); // Operation mode string bandName @enum("2.4 GHz", "5 GHz", "5 GHz (20 MHz)", "5 GHz (40 MHz)", "5 GHz (80 MHz)", "5 GHz (160 MHz)", "5.9 GHz") = default("2.4 GHz"); // Band name int channelNumber = default(0); // Initial channel number within the band (TODO this is offset by 1) string fcsMode @enum("declared","computed") = default("declared"); @@ -49,4 +49,3 @@ module Ieee80211Radio extends FlatRadioBase @signal[radioChannelChanged](type=long); @statistic[radioChannel](title="Radio channel"; source=radioChannelChanged; record=histogram,vector; interpolationmode=sample-hold); } - diff --git a/src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Receiver.ned b/src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Receiver.ned index c0c19e2afc2..29e9fc5cd80 100644 --- a/src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Receiver.ned +++ b/src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Receiver.ned @@ -23,7 +23,7 @@ import inet.physicallayer.wireless.common.base.packetlevel.NarrowbandReceiverBas module Ieee80211Receiver extends NarrowbandReceiverBase { parameters: - string opMode @enum("a","b","g(erp)","g(mixed)","n(mixed-2.4Ghz)","p","ac"); + string opMode @enum("a","b","g(erp)","g(mixed)","n(mixed-2.4Ghz)","n(greenfield-2.4Ghz)","p","ac"); string bandName @enum("2.4 GHz","5 GHz","5 GHz (20 MHz)","5 GHz (40 MHz)","5 GHz (80 MHz)","5 GHz (160 MHz)","5.9 GHz"); int channelNumber; modulation = default("BPSK"); // TODO this is simply wrong @@ -32,4 +32,3 @@ module Ieee80211Receiver extends NarrowbandReceiverBase *.opMode = this.opMode; @class(Ieee80211Receiver); } - diff --git a/src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Transmitter.ned b/src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Transmitter.ned index 3d50ab670e5..9c8b409b751 100644 --- a/src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Transmitter.ned +++ b/src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Transmitter.ned @@ -17,7 +17,7 @@ import inet.physicallayer.wireless.common.base.packetlevel.NarrowbandTransmitter module Ieee80211Transmitter extends NarrowbandTransmitterBase { parameters: - string opMode @enum("a","b","g(erp)","g(mixed)","n(mixed-2.4Ghz)","p","ac"); + string opMode @enum("a","b","g(erp)","g(mixed)","n(mixed-2.4Ghz)","n(greenfield-2.4Ghz)","p","ac"); string bandName @enum("2.4 GHz","5 GHz","5 GHz (20 MHz)","5 GHz (40 MHz)","5 GHz (80 MHz)","5 GHz (160 MHz)","5.9 GHz"); int channelNumber; modulation = default("BPSK"); // TODO: This is simply wrong diff --git a/tests/module/Ieee80211HtGreenfieldRuntime.test b/tests/module/Ieee80211HtGreenfieldRuntime.test new file mode 100644 index 00000000000..0e2db395db4 --- /dev/null +++ b/tests/module/Ieee80211HtGreenfieldRuntime.test @@ -0,0 +1,114 @@ +%description: + +Checks during one real packet exchange that Greenfield and mixed HT +operation select their corresponding preamble formats. + +%file: Test.cc +#include +#include + +#include "inet/common/Simsignals.h" +#include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.h" +#include "inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Radio.h" +#include "inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Transmission.h" + +using namespace inet; +using namespace inet::physicallayer; + +namespace Ieee80211HtGreenfieldRuntime { + +class HtPreambleCheckingRadio : public Ieee80211Radio, public cListener +{ + protected: + int htTransmissionCount = 0; + + virtual void initialize(int stage) override + { + Ieee80211Radio::initialize(stage); + if (stage == INITSTAGE_LOCAL) + subscribe(transmissionStartedSignal, this); + } + + virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) override + { + if (signalID != transmissionStartedSignal) + return; + auto transmission = check_and_cast(obj); + auto mode = dynamic_cast(transmission->getMode()); + if (mode == nullptr) + return; + htTransmissionCount++; + const char *opMode = par("opMode"); + auto expectedFormat = strcmp(opMode, "n(greenfield-2.4Ghz)") == 0 ? Ieee80211HtPreambleMode::HT_PREAMBLE_GREENFIELD : Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED; + const char *expectedPreamble = expectedFormat == Ieee80211HtPreambleMode::HT_PREAMBLE_GREENFIELD ? "greenfield" : "mixed"; + if (mode->getPreambleMode()->getPreambleFormat() != expectedFormat) + throw cRuntimeError("HT transmission used an unexpected preamble format (actual %d, expected %s for %s)", (int)mode->getPreambleMode()->getPreambleFormat(), expectedPreamble, opMode); + } + + virtual void finish() override + { + Ieee80211Radio::finish(); + if (htTransmissionCount == 0) + return; + const char *opMode = par("opMode"); + const char *preamble = strcmp(opMode, "n(greenfield-2.4Ghz)") == 0 ? "greenfield" : "mixed"; + std::cout << "Observed " << htTransmissionCount << " runtime HT " << preamble << " transmission(s).\n"; + } +}; + +Define_Module(HtPreambleCheckingRadio); + +} // namespace Ieee80211HtGreenfieldRuntime + +%file: test.ned + +import inet.examples.wireless.lan80211.Lan80211; +import inet.physicallayer.wireless.ieee80211.packetlevel.Ieee80211Radio; + +module HtPreambleCheckingRadio extends Ieee80211Radio +{ + parameters: + @class(HtPreambleCheckingRadio); +} + +network TestHtGreenfield extends Lan80211 +{ + parameters: + numHosts = default(2); +} + +%inifile: omnetpp.ini + +[General] +include ../../../../examples/wireless/lan80211/omnetpp-ht-greenfield.ini +network = TestHtGreenfield +abstract = false +ned-path = .;../../../../src;../../../../examples;../../lib +cmdenv-express-mode = true +record-vector-results = false +record-eventlog = false +seed-set = 0 + +**.wlan[*].radio.typename = "HtPreambleCheckingRadio" + +[Config HtPreambleRuntime] +abstract = false +network = TestHtGreenfield +sim-time-limit = 2s +*.numHosts = 2 +*.host[0].numApps = 1 +*.host[0].app[0].typename = "PingApp" +*.host[0].app[0].destAddr = "host[1]" +*.host[0].app[0].sendInterval = 100ms +*.host[1].numApps = 1 +*.host[1].app[0].typename = "PingApp" +*.host[1].app[0].destAddr = "host[0]" +*.host[1].app[0].sendInterval = 100ms +*.ap.wlan[*].opMode = "n(mixed-2.4Ghz)" +*.host[1].wlan[*].opMode = "n(mixed-2.4Ghz)" + +%extraargs: -c HtPreambleRuntime + +%contains-regex: stdout +Observed [1-9][0-9]* runtime HT greenfield transmission\(s\). +Observed [1-9][0-9]* runtime HT mixed transmission\(s\). diff --git a/tests/unit/Ieee80211HtGreenfield_1.test b/tests/unit/Ieee80211HtGreenfield_1.test new file mode 100644 index 00000000000..44b790b4a3f --- /dev/null +++ b/tests/unit/Ieee80211HtGreenfield_1.test @@ -0,0 +1,46 @@ +%description: +Checks that the 802.11n mixed and Greenfield mode profiles preserve distinct +preamble formats and timing when HT modes are cached. + +%includes: +#include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.h" +#include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h" + +%global: +using namespace inet; +using namespace inet::physicallayer; + +static const Ieee80211HtMode *findMcs0LongGiMode(const Ieee80211ModeSet *modeSet) +{ + auto mode = dynamic_cast(modeSet->getMode(Mbps(6.5), MHz(20), 1)); + return mode != nullptr && mode->getDataMode()->getModulationAndCodingScheme()->getMcsIndex() == 0 && + mode->getDataMode()->getGuardIntervalType() == Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG ? mode : nullptr; +} + +%activity: +auto mixedProfile = Ieee80211ModeSet::getModeSet("n(mixed-2.4Ghz)"); +auto greenfieldProfile = Ieee80211ModeSet::getModeSet("n(greenfield-2.4Ghz)"); +ASSERT(mixedProfile != nullptr); +ASSERT(greenfieldProfile != nullptr); +ASSERT(mixedProfile == Ieee80211ModeSet::getModeSet("n(mixed-2.4Ghz)")); +ASSERT(greenfieldProfile == Ieee80211ModeSet::getModeSet("n(greenfield-2.4Ghz)")); + +auto mixedMode = findMcs0LongGiMode(mixedProfile); +auto greenfieldMode = findMcs0LongGiMode(greenfieldProfile); +ASSERT(mixedMode != nullptr); +ASSERT(greenfieldMode != nullptr); +ASSERT(mixedMode != greenfieldMode); +ASSERT(mixedMode->getCenterFrequencyMode() == Ieee80211HtMode::BAND_2_4GHZ); +ASSERT(greenfieldMode->getCenterFrequencyMode() == Ieee80211HtMode::BAND_2_4GHZ); +ASSERT(mixedMode->getPreambleMode()->getPreambleFormat() == Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED); +ASSERT(greenfieldMode->getPreambleMode()->getPreambleFormat() == Ieee80211HtPreambleMode::HT_PREAMBLE_GREENFIELD); +ASSERT(mixedMode->getPreambleMode()->getDuration() == SimTime(36, SIMTIME_US)); +ASSERT(greenfieldMode->getPreambleMode()->getDuration() == SimTime(24, SIMTIME_US)); +ASSERT(mixedMode->getDuration(B(0)) == SimTime(40, SIMTIME_US)); +ASSERT(greenfieldMode->getDuration(B(0)) == SimTime(28, SIMTIME_US)); +ASSERT(mixedMode->getDuration(B(0)) - greenfieldMode->getDuration(B(0)) == SimTime(12, SIMTIME_US)); + +EV << "HT mixed and Greenfield modes have distinct cached identities and timing.\n"; + +%contains: stdout +HT mixed and Greenfield modes have distinct cached identities and timing. From f09a54a688e0fa7db802c1e2dd12a6b65374af96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Gonz=C3=A1lez=20L=C3=B3pez?= Date: Sat, 15 Aug 2026 15:07:21 +0200 Subject: [PATCH 2/4] Fix heterogeneous HT Greenfield response-rate selection HT mixed and Greenfield modes are cached as distinct objects because their PPDU preambles and timing differ. A received mode can therefore be semantically equivalent to a local mode-set entry without being pointer-identical, which previously caused response-rate selection to reject the mode as unknown. Resolve mode-set membership and lookup by PHY mode properties while preserving distinct preamble-specific mode objects. Select the HT-mixed profile for HT ACK, CTS, and BlockAck responses, as required for HT control responses, and apply the mapping to both legacy and QoS rate selection. Extend unit coverage for cross-profile lookup and mixed control-response selection, and update the heterogeneous runtime test to observe both HT preamble formats without assuming every HT transmission uses the local preferred format. Validation: make MODE=release -j12; inet_run_unit_tests -m release -f 'Ieee80211HtGreenfield_1\.test'; inet_run_module_tests -f 'Ieee80211HtGreenfieldRuntime\.test'. --- .../lan80211/omnetpp-ht-greenfield.ini | 1 - .../mac/rateselection/QosRateSelection.cc | 25 +++++---- .../mac/rateselection/RateSelection.cc | 13 +++-- .../ieee80211/mode/Ieee80211ModeSet.cc | 53 +++++++++++++++++++ .../ieee80211/mode/Ieee80211ModeSet.h | 6 ++- .../module/Ieee80211HtGreenfieldRuntime.test | 23 ++++---- tests/unit/Ieee80211HtGreenfield_1.test | 7 +++ 7 files changed, 97 insertions(+), 31 deletions(-) diff --git a/examples/wireless/lan80211/omnetpp-ht-greenfield.ini b/examples/wireless/lan80211/omnetpp-ht-greenfield.ini index 9cc212679f4..9d59b48fcc6 100644 --- a/examples/wireless/lan80211/omnetpp-ht-greenfield.ini +++ b/examples/wireless/lan80211/omnetpp-ht-greenfield.ini @@ -13,7 +13,6 @@ cmdenv-express-mode = true **.arp.typename = "GlobalArp" **.opMode = "n(greenfield-2.4Ghz)" **.wlan[*].bitrate = 13Mbps -**.wlan[*].radio.transmitter.centerFrequency = 2.4GHz *.ap.wlan[*].address = "10:00:00:00:00:00" *.host[*].**.mgmt.accessPointAddress = "10:00:00:00:00:00" diff --git a/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc b/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc index c8579ac4424..4602c71a02b 100644 --- a/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc +++ b/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc @@ -32,11 +32,11 @@ void QosRateSelection::initialize(int stage) double controlFrameBitrate = par("controlFrameBitrate"); controlFrameMode = (controlFrameBitrate == -1) ? nullptr : modeSet->getMode(bps(controlFrameBitrate)); double responseAckFrameBitrate = par("responseAckFrameBitrate"); - responseAckFrameMode = (responseAckFrameBitrate == -1) ? nullptr : modeSet->getMode(bps(responseAckFrameBitrate)); + responseAckFrameMode = (responseAckFrameBitrate == -1) ? nullptr : modeSet->getControlResponseMode(modeSet->getMode(bps(responseAckFrameBitrate))); double responseBlockAckFrameBitrate = par("responseBlockAckFrameBitrate"); - responseBlockAckFrameMode = (responseBlockAckFrameBitrate == -1) ? nullptr : modeSet->getMode(bps(responseBlockAckFrameBitrate)); + responseBlockAckFrameMode = (responseBlockAckFrameBitrate == -1) ? nullptr : modeSet->getControlResponseMode(modeSet->getMode(bps(responseBlockAckFrameBitrate))); double responseCtsFrameBitrate = par("responseCtsFrameBitrate"); - responseCtsFrameMode = (responseCtsFrameBitrate == -1) ? nullptr : modeSet->getMode(bps(responseCtsFrameBitrate)); + responseCtsFrameMode = (responseCtsFrameBitrate == -1) ? nullptr : modeSet->getControlResponseMode(modeSet->getMode(bps(responseCtsFrameBitrate))); } } @@ -75,9 +75,11 @@ const IIeee80211Mode *QosRateSelection::computeResponseAckFrameMode(Packet *pack auto mode = getMode(packet, dataOrMgmtHeader); ASSERT(modeSet->containsMode(mode)); if (!responseAckFrameMode) { - if (modeSet->getIsMandatory(mode)) - return mode; - else if (auto slowerMode = modeSet->getSlowerMandatoryMode(mode)) + auto responseModeSet = modeSet->getControlResponseModeSet(mode); + auto responseMode = responseModeSet->getMode(mode); + if (responseModeSet->getIsMandatory(responseMode)) + return responseMode; + else if (auto slowerMode = responseModeSet->getSlowerMandatoryMode(responseMode)) return slowerMode; else throw cRuntimeError("Mandatory mode not found"); @@ -92,9 +94,11 @@ const IIeee80211Mode *QosRateSelection::computeResponseCtsFrameMode(Packet *pack auto mode = getMode(packet, rtsFrame); ASSERT(modeSet->containsMode(mode)); if (!responseCtsFrameMode) { - if (modeSet->getIsMandatory(mode)) - return mode; - else if (auto slowerMode = modeSet->getSlowerMandatoryMode(mode)) + auto responseModeSet = modeSet->getControlResponseModeSet(mode); + auto responseMode = responseModeSet->getMode(mode); + if (responseModeSet->getIsMandatory(responseMode)) + return responseMode; + else if (auto slowerMode = responseModeSet->getSlowerMandatoryMode(responseMode)) return slowerMode; else throw cRuntimeError("Mandatory mode not found"); @@ -112,7 +116,7 @@ const IIeee80211Mode *QosRateSelection::computeResponseCtsFrameMode(Packet *pack const IIeee80211Mode *QosRateSelection::computeResponseBlockAckFrameMode(Packet *packet, const Ptr& blockAckReq) { if (dynamicPtrCast(blockAckReq)) - return responseBlockAckFrameMode ? responseBlockAckFrameMode : getMode(packet, blockAckReq); + return responseBlockAckFrameMode ? responseBlockAckFrameMode : modeSet->getControlResponseMode(getMode(packet, blockAckReq)); else throw cRuntimeError("Unknown BlockAckReq frame type"); } @@ -248,4 +252,3 @@ void QosRateSelection::frameTransmitted(Packet *packet, const PtrgetMode(bps(controlFrameBitrate)); double responseAckFrameBitrate = par("responseAckFrameBitrate"); - responseAckFrameMode = (responseAckFrameBitrate == -1) ? nullptr : modeSet->getMode(bps(responseAckFrameBitrate)); + responseAckFrameMode = (responseAckFrameBitrate == -1) ? nullptr : modeSet->getControlResponseMode(modeSet->getMode(bps(responseAckFrameBitrate))); double responseCtsFrameBitrate = par("responseCtsFrameBitrate"); - responseCtsFrameMode = (responseCtsFrameBitrate == -1) ? nullptr : modeSet->getMode(bps(responseCtsFrameBitrate)); + responseCtsFrameMode = (responseCtsFrameBitrate == -1) ? nullptr : modeSet->getControlResponseMode(modeSet->getMode(bps(responseCtsFrameBitrate))); fastestMandatoryMode = modeSet->getFastestMandatoryMode(); // WATCH(dataOrMgmtRateControl); @@ -84,7 +84,9 @@ const IIeee80211Mode *RateSelection::computeResponseAckFrameMode(Packet *packet, else { auto mode = getMode(packet, dataOrMgmtHeader); ASSERT(modeSet->containsMode(mode)); - return modeSet->getIsMandatory(mode) ? mode : modeSet->getSlowerMandatoryMode(mode); // TODO BSSBasicRateSet + auto responseModeSet = modeSet->getControlResponseModeSet(mode); + auto responseMode = responseModeSet->getMode(mode); + return responseModeSet->getIsMandatory(responseMode) ? responseMode : responseModeSet->getSlowerMandatoryMode(responseMode); // TODO BSSBasicRateSet } } @@ -95,7 +97,9 @@ const IIeee80211Mode *RateSelection::computeResponseCtsFrameMode(Packet *packet, else { auto mode = getMode(packet, rtsFrame); ASSERT(modeSet->containsMode(mode)); - return modeSet->getIsMandatory(mode) ? mode : modeSet->getSlowerMandatoryMode(mode); // TODO BSSBasicRateSet + auto responseModeSet = modeSet->getControlResponseModeSet(mode); + auto responseMode = responseModeSet->getMode(mode); + return responseModeSet->getIsMandatory(responseMode) ? responseMode : responseModeSet->getSlowerMandatoryMode(responseMode); // TODO BSSBasicRateSet } } @@ -170,4 +174,3 @@ void RateSelection::setFrameMode(Packet *packet, const Ptr +#include #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211DsssMode.h" #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ErpOfdmMode.h" @@ -496,6 +497,30 @@ int Ieee80211ModeSet::findModeIndex(const IIeee80211Mode *mode) const for (size_t index = 0; index < entries.size(); index++) if (entries[index].mode == mode) return index; + // HT mixed and Greenfield modes have distinct cached identities because their + // preambles and timing differ, but they represent the same data-rate mode for + // mode-set membership and rate selection purposes. + if (mode != nullptr) { + for (size_t index = 0; index < entries.size(); index++) { + auto entryMode = entries[index].mode; + auto entryHeaderMode = entryMode->getHeaderMode(); + auto modeHeaderMode = mode->getHeaderMode(); + auto entryDataMode = entryMode->getDataMode(); + auto modeDataMode = mode->getDataMode(); + if (typeid(*entryMode) == typeid(*mode) && + entryHeaderMode->getNetBitrate() == modeHeaderMode->getNetBitrate() && + entryHeaderMode->getGrossBitrate() == modeHeaderMode->getGrossBitrate() && + entryHeaderMode->getSymbolInterval() == modeHeaderMode->getSymbolInterval() && + entryDataMode->getBandwidth() == modeDataMode->getBandwidth() && + entryDataMode->getNetBitrate() == modeDataMode->getNetBitrate() && + entryDataMode->getGrossBitrate() == modeDataMode->getGrossBitrate() && + entryDataMode->getSymbolInterval() == modeDataMode->getSymbolInterval() && + entryDataMode->getNumberOfSpatialStreams() == modeDataMode->getNumberOfSpatialStreams()) + { + return index; + } + } + } return -1; } @@ -513,6 +538,20 @@ bool Ieee80211ModeSet::getIsMandatory(const IIeee80211Mode *mode) const return entries[getModeIndex(mode)].isMandatory; } +const IIeee80211Mode *Ieee80211ModeSet::findMode(const IIeee80211Mode *mode) const +{ + int index = findModeIndex(mode); + return index >= 0 ? entries[index].mode : nullptr; +} + +const IIeee80211Mode *Ieee80211ModeSet::getMode(const IIeee80211Mode *mode) const +{ + auto result = findMode(mode); + if (result == nullptr) + throw cRuntimeError("Unknown mode in operation mode: '%s'", getName()); + return result; +} + const IIeee80211Mode *Ieee80211ModeSet::findMode(bps bitrate, Hz bandwidth, int numSpatialStreams) const { return findMode(bitrate - Mbps(0.05), bitrate + Mbps(0.05), bandwidth, numSpatialStreams); @@ -616,6 +655,20 @@ const IIeee80211Mode *Ieee80211ModeSet::getFasterMandatoryMode(const IIeee80211M return nullptr; } +const Ieee80211ModeSet *Ieee80211ModeSet::getControlResponseModeSet(const IIeee80211Mode *mode) const +{ + // IEEE 802.11 prohibits HT-GF format for control response frames; use the + // HT-mixed profile while retaining the received mode's rate parameters. + if (dynamic_cast(mode) != nullptr) + return getModeSet("n(mixed-2.4Ghz)"); + return this; +} + +const IIeee80211Mode *Ieee80211ModeSet::getControlResponseMode(const IIeee80211Mode *mode) const +{ + return getControlResponseModeSet(mode)->getMode(mode); +} + const Ieee80211ModeSet *Ieee80211ModeSet::findModeSet(const char *mode) { for (size_t index = 0; index < (&modeSets)->size(); index++) { diff --git a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h index 98155bf77f6..19e861d833c 100644 --- a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h +++ b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h @@ -52,6 +52,8 @@ class INET_API Ieee80211ModeSet : public IPrintableObject, public cObject bool containsMode(const IIeee80211Mode *mode) const { return findModeIndex(mode) != -1; } bool getIsMandatory(const IIeee80211Mode *mode) const; + const IIeee80211Mode *findMode(const IIeee80211Mode *mode) const; + const IIeee80211Mode *getMode(const IIeee80211Mode *mode) const; const IIeee80211Mode *findMode(bps bitrate, Hz bandwidth = Hz(NaN), int numSpatialStreams = -1) const; const IIeee80211Mode *findMode(bps minBitrate, bps maxBitrate, Hz bandwidth = Hz(NaN), int numSpatialStreams = -1) const; const IIeee80211Mode *getMode(bps bitrate, Hz bandwidth = Hz(NaN), int numSpatialStreams = -1) const; @@ -65,6 +67,9 @@ class INET_API Ieee80211ModeSet : public IPrintableObject, public cObject const IIeee80211Mode *getSlowerMandatoryMode(const IIeee80211Mode *mode) const; const IIeee80211Mode *getFasterMandatoryMode(const IIeee80211Mode *mode) const; + const Ieee80211ModeSet *getControlResponseModeSet(const IIeee80211Mode *mode) const; + const IIeee80211Mode *getControlResponseMode(const IIeee80211Mode *mode) const; + static const Ieee80211ModeSet *findModeSet(const char *mode); static const Ieee80211ModeSet *getModeSet(const char *mode); @@ -84,4 +89,3 @@ class INET_API Ieee80211ModeSet : public IPrintableObject, public cObject } // namespace inet #endif - diff --git a/tests/module/Ieee80211HtGreenfieldRuntime.test b/tests/module/Ieee80211HtGreenfieldRuntime.test index 0e2db395db4..1699371f3e0 100644 --- a/tests/module/Ieee80211HtGreenfieldRuntime.test +++ b/tests/module/Ieee80211HtGreenfieldRuntime.test @@ -4,7 +4,6 @@ Checks during one real packet exchange that Greenfield and mixed HT operation select their corresponding preamble formats. %file: Test.cc -#include #include #include "inet/common/Simsignals.h" @@ -20,7 +19,8 @@ namespace Ieee80211HtGreenfieldRuntime { class HtPreambleCheckingRadio : public Ieee80211Radio, public cListener { protected: - int htTransmissionCount = 0; + int htGreenfieldTransmissionCount = 0; + int htMixedTransmissionCount = 0; virtual void initialize(int stage) override { @@ -37,22 +37,19 @@ class HtPreambleCheckingRadio : public Ieee80211Radio, public cListener auto mode = dynamic_cast(transmission->getMode()); if (mode == nullptr) return; - htTransmissionCount++; - const char *opMode = par("opMode"); - auto expectedFormat = strcmp(opMode, "n(greenfield-2.4Ghz)") == 0 ? Ieee80211HtPreambleMode::HT_PREAMBLE_GREENFIELD : Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED; - const char *expectedPreamble = expectedFormat == Ieee80211HtPreambleMode::HT_PREAMBLE_GREENFIELD ? "greenfield" : "mixed"; - if (mode->getPreambleMode()->getPreambleFormat() != expectedFormat) - throw cRuntimeError("HT transmission used an unexpected preamble format (actual %d, expected %s for %s)", (int)mode->getPreambleMode()->getPreambleFormat(), expectedPreamble, opMode); + if (mode->getPreambleMode()->getPreambleFormat() == Ieee80211HtPreambleMode::HT_PREAMBLE_GREENFIELD) + htGreenfieldTransmissionCount++; + else if (mode->getPreambleMode()->getPreambleFormat() == Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED) + htMixedTransmissionCount++; } virtual void finish() override { Ieee80211Radio::finish(); - if (htTransmissionCount == 0) - return; - const char *opMode = par("opMode"); - const char *preamble = strcmp(opMode, "n(greenfield-2.4Ghz)") == 0 ? "greenfield" : "mixed"; - std::cout << "Observed " << htTransmissionCount << " runtime HT " << preamble << " transmission(s).\n"; + if (htGreenfieldTransmissionCount > 0) + std::cout << "Observed " << htGreenfieldTransmissionCount << " runtime HT greenfield transmission(s).\n"; + if (htMixedTransmissionCount > 0) + std::cout << "Observed " << htMixedTransmissionCount << " runtime HT mixed transmission(s).\n"; } }; diff --git a/tests/unit/Ieee80211HtGreenfield_1.test b/tests/unit/Ieee80211HtGreenfield_1.test index 44b790b4a3f..2a686ac0dc1 100644 --- a/tests/unit/Ieee80211HtGreenfield_1.test +++ b/tests/unit/Ieee80211HtGreenfield_1.test @@ -39,6 +39,13 @@ ASSERT(greenfieldMode->getPreambleMode()->getDuration() == SimTime(24, SIMTIME_U ASSERT(mixedMode->getDuration(B(0)) == SimTime(40, SIMTIME_US)); ASSERT(greenfieldMode->getDuration(B(0)) == SimTime(28, SIMTIME_US)); ASSERT(mixedMode->getDuration(B(0)) - greenfieldMode->getDuration(B(0)) == SimTime(12, SIMTIME_US)); +ASSERT(mixedProfile->containsMode(greenfieldMode)); +ASSERT(greenfieldProfile->containsMode(mixedMode)); +ASSERT(mixedProfile->getMode(greenfieldMode) == mixedMode); +ASSERT(greenfieldProfile->getMode(mixedMode) == greenfieldMode); +ASSERT(mixedProfile->getControlResponseMode(mixedMode) == mixedMode); +ASSERT(greenfieldProfile->getControlResponseModeSet(greenfieldMode) == mixedProfile); +ASSERT(greenfieldProfile->getControlResponseMode(greenfieldMode) == mixedMode); EV << "HT mixed and Greenfield modes have distinct cached identities and timing.\n"; From 971e4ffc9a304e09862910e97cba977820215337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Gonz=C3=A1lez=20L=C3=B3pez?= Date: Sat, 15 Aug 2026 17:36:15 +0200 Subject: [PATCH 3/4] Fix HT control-response mode matching Restrict cross-profile mode equivalence to HT modes and compare the MCS index, bandwidth, guard interval, frequency band, and spatial stream count while intentionally ignoring the mixed/Greenfield preamble format. Discover the control-response mode set from the registered same-band HT-mixed profiles instead of hardcoding the 2.4 GHz profile, and fail explicitly when the counterpart is missing or ambiguous. Keep configured ACK, CTS, and BlockAck modes owned by each rate selector's active mode set, translating them to the control-response profile only when the response is computed. Add focused coverage for HT mixed/Greenfield mapping, 5 GHz rejection without a registered counterpart, strict ERP mode membership, and configured DCF/QoS response-mode caching and conversion. --- .../mac/rateselection/QosRateSelection.cc | 18 ++- .../mac/rateselection/RateSelection.cc | 8 +- .../ieee80211/mode/Ieee80211ModeSet.cc | 55 ++++---- ...e80211ConfiguredResponseRateSelection.test | 122 ++++++++++++++++++ tests/unit/Ieee80211HtGreenfield_1.test | 55 +++++++- 5 files changed, 224 insertions(+), 34 deletions(-) create mode 100644 tests/module/Ieee80211ConfiguredResponseRateSelection.test diff --git a/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc b/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc index 4602c71a02b..b191ad9904d 100644 --- a/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc +++ b/src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc @@ -32,11 +32,11 @@ void QosRateSelection::initialize(int stage) double controlFrameBitrate = par("controlFrameBitrate"); controlFrameMode = (controlFrameBitrate == -1) ? nullptr : modeSet->getMode(bps(controlFrameBitrate)); double responseAckFrameBitrate = par("responseAckFrameBitrate"); - responseAckFrameMode = (responseAckFrameBitrate == -1) ? nullptr : modeSet->getControlResponseMode(modeSet->getMode(bps(responseAckFrameBitrate))); + responseAckFrameMode = (responseAckFrameBitrate == -1) ? nullptr : modeSet->getMode(bps(responseAckFrameBitrate)); double responseBlockAckFrameBitrate = par("responseBlockAckFrameBitrate"); - responseBlockAckFrameMode = (responseBlockAckFrameBitrate == -1) ? nullptr : modeSet->getControlResponseMode(modeSet->getMode(bps(responseBlockAckFrameBitrate))); + responseBlockAckFrameMode = (responseBlockAckFrameBitrate == -1) ? nullptr : modeSet->getMode(bps(responseBlockAckFrameBitrate)); double responseCtsFrameBitrate = par("responseCtsFrameBitrate"); - responseCtsFrameMode = (responseCtsFrameBitrate == -1) ? nullptr : modeSet->getControlResponseMode(modeSet->getMode(bps(responseCtsFrameBitrate))); + responseCtsFrameMode = (responseCtsFrameBitrate == -1) ? nullptr : modeSet->getMode(bps(responseCtsFrameBitrate)); } } @@ -85,7 +85,7 @@ const IIeee80211Mode *QosRateSelection::computeResponseAckFrameMode(Packet *pack throw cRuntimeError("Mandatory mode not found"); } else - return responseAckFrameMode; + return modeSet->getControlResponseMode(responseAckFrameMode); } const IIeee80211Mode *QosRateSelection::computeResponseCtsFrameMode(Packet *packet, const Ptr& rtsFrame) @@ -104,7 +104,7 @@ const IIeee80211Mode *QosRateSelection::computeResponseCtsFrameMode(Packet *pack throw cRuntimeError("Mandatory mode not found"); } else - return responseCtsFrameMode; + return modeSet->getControlResponseMode(responseCtsFrameMode); } // @@ -115,8 +115,12 @@ const IIeee80211Mode *QosRateSelection::computeResponseCtsFrameMode(Packet *pack // const IIeee80211Mode *QosRateSelection::computeResponseBlockAckFrameMode(Packet *packet, const Ptr& blockAckReq) { - if (dynamicPtrCast(blockAckReq)) - return responseBlockAckFrameMode ? responseBlockAckFrameMode : modeSet->getControlResponseMode(getMode(packet, blockAckReq)); + if (dynamicPtrCast(blockAckReq)) { + if (responseBlockAckFrameMode) + return modeSet->getControlResponseMode(responseBlockAckFrameMode); + else + return modeSet->getControlResponseMode(getMode(packet, blockAckReq)); + } else throw cRuntimeError("Unknown BlockAckReq frame type"); } diff --git a/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.cc b/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.cc index d3671871d57..2d9a1482401 100644 --- a/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.cc +++ b/src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.cc @@ -38,9 +38,9 @@ void RateSelection::initialize(int stage) double controlFrameBitrate = par("controlFrameBitrate"); controlFrameMode = (controlFrameBitrate == -1) ? nullptr : modeSet->getMode(bps(controlFrameBitrate)); double responseAckFrameBitrate = par("responseAckFrameBitrate"); - responseAckFrameMode = (responseAckFrameBitrate == -1) ? nullptr : modeSet->getControlResponseMode(modeSet->getMode(bps(responseAckFrameBitrate))); + responseAckFrameMode = (responseAckFrameBitrate == -1) ? nullptr : modeSet->getMode(bps(responseAckFrameBitrate)); double responseCtsFrameBitrate = par("responseCtsFrameBitrate"); - responseCtsFrameMode = (responseCtsFrameBitrate == -1) ? nullptr : modeSet->getControlResponseMode(modeSet->getMode(bps(responseCtsFrameBitrate))); + responseCtsFrameMode = (responseCtsFrameBitrate == -1) ? nullptr : modeSet->getMode(bps(responseCtsFrameBitrate)); fastestMandatoryMode = modeSet->getFastestMandatoryMode(); // WATCH(dataOrMgmtRateControl); @@ -80,7 +80,7 @@ const IIeee80211Mode *RateSelection::getMode(Packet *packet, const Ptr& dataOrMgmtHeader) { if (responseAckFrameMode) - return responseAckFrameMode; + return modeSet->getControlResponseMode(responseAckFrameMode); else { auto mode = getMode(packet, dataOrMgmtHeader); ASSERT(modeSet->containsMode(mode)); @@ -93,7 +93,7 @@ const IIeee80211Mode *RateSelection::computeResponseAckFrameMode(Packet *packet, const IIeee80211Mode *RateSelection::computeResponseCtsFrameMode(Packet *packet, const Ptr& rtsFrame) { if (responseCtsFrameMode) - return responseCtsFrameMode; + return modeSet->getControlResponseMode(responseCtsFrameMode); else { auto mode = getMode(packet, rtsFrame); ASSERT(modeSet->containsMode(mode)); diff --git a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.cc b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.cc index b0d4c95c1f2..7ea1164aa78 100644 --- a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.cc +++ b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.cc @@ -8,7 +8,6 @@ #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h" #include -#include #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211DsssMode.h" #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ErpOfdmMode.h" @@ -500,24 +499,20 @@ int Ieee80211ModeSet::findModeIndex(const IIeee80211Mode *mode) const // HT mixed and Greenfield modes have distinct cached identities because their // preambles and timing differ, but they represent the same data-rate mode for // mode-set membership and rate selection purposes. - if (mode != nullptr) { + if (auto htMode = dynamic_cast(mode)) { + auto htDataMode = htMode->getDataMode(); for (size_t index = 0; index < entries.size(); index++) { - auto entryMode = entries[index].mode; - auto entryHeaderMode = entryMode->getHeaderMode(); - auto modeHeaderMode = mode->getHeaderMode(); - auto entryDataMode = entryMode->getDataMode(); - auto modeDataMode = mode->getDataMode(); - if (typeid(*entryMode) == typeid(*mode) && - entryHeaderMode->getNetBitrate() == modeHeaderMode->getNetBitrate() && - entryHeaderMode->getGrossBitrate() == modeHeaderMode->getGrossBitrate() && - entryHeaderMode->getSymbolInterval() == modeHeaderMode->getSymbolInterval() && - entryDataMode->getBandwidth() == modeDataMode->getBandwidth() && - entryDataMode->getNetBitrate() == modeDataMode->getNetBitrate() && - entryDataMode->getGrossBitrate() == modeDataMode->getGrossBitrate() && - entryDataMode->getSymbolInterval() == modeDataMode->getSymbolInterval() && - entryDataMode->getNumberOfSpatialStreams() == modeDataMode->getNumberOfSpatialStreams()) - { - return index; + auto entryHtMode = dynamic_cast(entries[index].mode); + if (entryHtMode != nullptr) { + auto entryHtDataMode = entryHtMode->getDataMode(); + if (entryHtDataMode->getMcsIndex() == htDataMode->getMcsIndex() && + entryHtDataMode->getBandwidth() == htDataMode->getBandwidth() && + entryHtDataMode->getGuardIntervalType() == htDataMode->getGuardIntervalType() && + entryHtMode->getCenterFrequencyMode() == htMode->getCenterFrequencyMode() && + entryHtDataMode->getNumberOfSpatialStreams() == htDataMode->getNumberOfSpatialStreams()) + { + return index; + } } } } @@ -657,10 +652,26 @@ const IIeee80211Mode *Ieee80211ModeSet::getFasterMandatoryMode(const IIeee80211M const Ieee80211ModeSet *Ieee80211ModeSet::getControlResponseModeSet(const IIeee80211Mode *mode) const { - // IEEE 802.11 prohibits HT-GF format for control response frames; use the - // HT-mixed profile while retaining the received mode's rate parameters. - if (dynamic_cast(mode) != nullptr) - return getModeSet("n(mixed-2.4Ghz)"); + // IEEE 802.11 prohibits HT-GF format for control response frames; use a + // same-band HT-mixed profile while retaining the received mode's rate parameters. + if (auto htMode = dynamic_cast(mode)) { + const Ieee80211ModeSet *controlResponseModeSet = nullptr; + for (size_t index = 0; index < (&modeSets)->size(); index++) { + auto candidateModeSet = &(&modeSets)->at(index); + auto candidateMode = dynamic_cast(candidateModeSet->findMode(mode)); + if (candidateMode != nullptr && + candidateMode->getPreambleMode()->getPreambleFormat() == Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED && + candidateMode->getCenterFrequencyMode() == htMode->getCenterFrequencyMode()) + { + if (controlResponseModeSet != nullptr) + throw cRuntimeError("Multiple same-band HT-mixed control response mode sets for mode: '%s'", mode->getName()); + controlResponseModeSet = candidateModeSet; + } + } + if (controlResponseModeSet == nullptr) + throw cRuntimeError("No same-band HT-mixed control response mode set for mode: '%s'", mode->getName()); + return controlResponseModeSet; + } return this; } diff --git a/tests/module/Ieee80211ConfiguredResponseRateSelection.test b/tests/module/Ieee80211ConfiguredResponseRateSelection.test new file mode 100644 index 00000000000..02a1ae0c711 --- /dev/null +++ b/tests/module/Ieee80211ConfiguredResponseRateSelection.test @@ -0,0 +1,122 @@ +%description: +Checks that configured response rates are cached as local modes and converted +to HT-mixed control-response modes at compute time for DCF and QoS rate +selection, including a Basic BlockAck response. + +%file: Test.cc +#include + +#include "inet/common/InitStages.h" +#include "inet/common/packet/Packet.h" +#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h" +#define protected public // test-only access to verify initialized response-mode ownership +#include "inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.h" +#include "inet/linklayer/ieee80211/mac/rateselection/RateSelection.h" +#undef protected +#include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.h" +#include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h" +#include "inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Tag_m.h" + +using namespace inet; +using namespace inet::ieee80211; +using namespace inet::physicallayer; + +namespace Ieee80211ConfiguredResponseRateSelection { + +static void assertConfiguredResponse(const char *name, const IIeee80211Mode *mode) +{ + auto htMode = dynamic_cast(mode); + if (htMode == nullptr) + throw cRuntimeError("%s did not return an HT mode", name); + if (htMode->getPreambleMode()->getPreambleFormat() != Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED) + throw cRuntimeError("%s did not return an HT-mixed mode", name); + if (htMode->getDataMode()->getNetBitrate() != Mbps(6.5)) + throw cRuntimeError("%s did not return 6.5 Mbps", name); +} + +class ConfiguredResponseProbe : public cSimpleModule +{ + protected: + virtual int numInitStages() const override { return NUM_INIT_STAGES; } + + virtual void initialize(int stage) override + { + if (stage != INITSTAGE_LAST) + return; + + auto dcfRateSelection = check_and_cast(getSimulation()->getModuleByPath("TestConfiguredResponses.host[0].wlan[0].mac.dcf.rateSelection")); + auto qosRateSelection = check_and_cast(getSimulation()->getModuleByPath("TestConfiguredResponses.host[0].wlan[0].mac.hcf.rateSelection")); + + if (double(dcfRateSelection->par("responseAckFrameBitrate")) != 6.5e6 + || double(qosRateSelection->par("responseAckFrameBitrate")) != 6.5e6) + throw cRuntimeError("Configured response bitrate was not applied to both rate selections"); + + auto sourceMode = Ieee80211ModeSet::getModeSet("n(greenfield-2.4Ghz)")->getMode(Mbps(6.5)); + if (dcfRateSelection->responseAckFrameMode != sourceMode || dcfRateSelection->responseCtsFrameMode != sourceMode + || qosRateSelection->responseAckFrameMode != sourceMode || qosRateSelection->responseCtsFrameMode != sourceMode + || qosRateSelection->responseBlockAckFrameMode != sourceMode) + throw cRuntimeError("Configured response modes were not cached as local Greenfield modes"); + Packet packet("receivedFrame"); + packet.addTag()->setMode(sourceMode); + + assertConfiguredResponse("DCF ACK", dcfRateSelection->computeResponseAckFrameMode(nullptr, nullptr)); + assertConfiguredResponse("DCF CTS", dcfRateSelection->computeResponseCtsFrameMode(nullptr, nullptr)); + assertConfiguredResponse("QoS ACK", qosRateSelection->computeResponseAckFrameMode(&packet, nullptr)); + assertConfiguredResponse("QoS CTS", qosRateSelection->computeResponseCtsFrameMode(&packet, nullptr)); + + auto basicBlockAckReq = makeShared(); + assertConfiguredResponse("QoS Basic BlockAck", qosRateSelection->computeResponseBlockAckFrameMode(&packet, basicBlockAckReq)); + + std::cout << "Configured DCF and QoS ACK/CTS/Basic BlockAck responses use HT-mixed 6.5 Mbps modes.\n"; + } +}; + +Define_Module(ConfiguredResponseProbe); + +} // namespace Ieee80211ConfiguredResponseRateSelection + +%file: test.ned + +import inet.examples.wireless.lan80211.Lan80211; + +simple ConfiguredResponseProbe +{ + @class(ConfiguredResponseProbe); +} + +network TestConfiguredResponses extends Lan80211 +{ + parameters: + numHosts = default(2); + submodules: + configuredResponseProbe: ConfiguredResponseProbe; +} + +%inifile: omnetpp.ini + +[General] +include ../../../../examples/wireless/lan80211/omnetpp-ht-greenfield.ini +network = TestConfiguredResponses +abstract = false +ned-path = .;../../../../src;../../../../examples;../../lib +cmdenv-express-mode = true +record-vector-results = false +record-eventlog = false +seed-set = 0 + +[Config ConfiguredResponses] +abstract = false +network = TestConfiguredResponses +sim-time-limit = 1s +**.opMode = "n(greenfield-2.4Ghz)" +**.qosStation = true +**.wlan[*].mac.dcf.rateSelection.responseAckFrameBitrate = 6.5Mbps +**.wlan[*].mac.dcf.rateSelection.responseCtsFrameBitrate = 6.5Mbps +**.wlan[*].mac.hcf.rateSelection.responseAckFrameBitrate = 6.5Mbps +**.wlan[*].mac.hcf.rateSelection.responseCtsFrameBitrate = 6.5Mbps +**.wlan[*].mac.hcf.rateSelection.responseBlockAckFrameBitrate = 6.5Mbps + +%extraargs: -c ConfiguredResponses + +%contains: stdout +Configured DCF and QoS ACK/CTS/Basic BlockAck responses use HT-mixed 6.5 Mbps modes. diff --git a/tests/unit/Ieee80211HtGreenfield_1.test b/tests/unit/Ieee80211HtGreenfield_1.test index 2a686ac0dc1..c73d94e0502 100644 --- a/tests/unit/Ieee80211HtGreenfield_1.test +++ b/tests/unit/Ieee80211HtGreenfield_1.test @@ -1,10 +1,14 @@ %description: Checks that the 802.11n mixed and Greenfield mode profiles preserve distinct -preamble formats and timing when HT modes are cached. +preamble formats and timing while supporting cross-profile lookup, and that +legacy ERP mode profiles retain strict mode membership. %includes: +#include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211DsssOfdmMode.h" +#include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ErpOfdmMode.h" #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.h" #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h" +#include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211OfdmMode.h" %global: using namespace inet; @@ -17,6 +21,28 @@ static const Ieee80211HtMode *findMcs0LongGiMode(const Ieee80211ModeSet *modeSet mode->getDataMode()->getGuardIntervalType() == Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG ? mode : nullptr; } +static bool rejectsUnknownMode(const Ieee80211ModeSet *modeSet, const IIeee80211Mode *mode) +{ + try { + modeSet->getMode(mode); + return false; + } + catch (const cRuntimeError&) { + return true; + } +} + +static bool rejectsControlResponseModeSet(const Ieee80211ModeSet *modeSet, const IIeee80211Mode *mode) +{ + try { + modeSet->getControlResponseModeSet(mode); + return false; + } + catch (const cRuntimeError&) { + return true; + } +} + %activity: auto mixedProfile = Ieee80211ModeSet::getModeSet("n(mixed-2.4Ghz)"); auto greenfieldProfile = Ieee80211ModeSet::getModeSet("n(greenfield-2.4Ghz)"); @@ -47,6 +73,33 @@ ASSERT(mixedProfile->getControlResponseMode(mixedMode) == mixedMode); ASSERT(greenfieldProfile->getControlResponseModeSet(greenfieldMode) == mixedProfile); ASSERT(greenfieldProfile->getControlResponseMode(greenfieldMode) == mixedMode); +auto greenfield5GhzMode = Ieee80211HtCompliantModes::getCompliantMode( + &Ieee80211HtmcsTable::htMcs0BW20MHz, + Ieee80211HtMode::BAND_5GHZ, + Ieee80211HtPreambleMode::HT_PREAMBLE_GREENFIELD, + Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG); +ASSERT(greenfield5GhzMode->getCenterFrequencyMode() == Ieee80211HtMode::BAND_5GHZ); +ASSERT(greenfield5GhzMode->getPreambleMode()->getPreambleFormat() == Ieee80211HtPreambleMode::HT_PREAMBLE_GREENFIELD); +ASSERT(rejectsControlResponseModeSet(greenfieldProfile, greenfield5GhzMode)); + +auto erpMixedProfile = Ieee80211ModeSet::getModeSet("g(mixed)"); +auto erpOnlyProfile = Ieee80211ModeSet::getModeSet("g(erp)"); +auto erpMixedMode = &Ieee80211ErpOfdmCompliantModes::erpOfdmMode6Mbps; +auto erpOnlyMode = &Ieee80211ErpOfdmCompliantModes::erpOnlyOfdmMode6Mbps; +Ieee80211DsssOfdmMode dsssOfdmMode("dsss-ofdm", nullptr, nullptr, nullptr, nullptr, &Ieee80211OfdmCompliantModes::ofdmDataMode6MbpsCS20MHz); +ASSERT(erpMixedProfile != nullptr); +ASSERT(erpOnlyProfile != nullptr); +ASSERT(erpMixedMode != erpOnlyMode); +ASSERT(erpMixedMode->getSlotTime() == SimTime(20, SIMTIME_US)); +ASSERT(erpOnlyMode->getSlotTime() == SimTime(9, SIMTIME_US)); +ASSERT(!erpMixedProfile->containsMode(erpOnlyMode)); +ASSERT(!erpOnlyProfile->containsMode(erpMixedMode)); +ASSERT(erpMixedProfile->findMode(erpOnlyMode) == nullptr); +ASSERT(erpOnlyProfile->findMode(erpMixedMode) == nullptr); +ASSERT(erpMixedProfile->findMode(&dsssOfdmMode) == nullptr); +ASSERT(rejectsUnknownMode(erpMixedProfile, erpOnlyMode)); +ASSERT(rejectsUnknownMode(erpOnlyProfile, erpMixedMode)); + EV << "HT mixed and Greenfield modes have distinct cached identities and timing.\n"; %contains: stdout From ec396c45c6afb0f3b242db0fcd472ea29ccc9ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Gonz=C3=A1lez=20L=C3=B3pez?= Date: Sun, 16 Aug 2026 02:46:41 +0200 Subject: [PATCH 4/4] 802.11: keep HT mode membership strict Restore exact pointer identity for mode-set membership so receiver feasibility checks and transmitter validation do not treat HT mixed and Greenfield modes as interchangeable. Add a separate HT-equivalence lookup for control-response format translation and cache successful control-response resolutions. Preserve same-band mixed-format selection while rejecting modes outside the configured operation mode, including unsupported 5 GHz HT inputs. Include band and preamble format in the VHT compliant-mode cache key. Update the HT unit and runtime coverage, and remove the protected-to-public preprocessor workaround from the configured response-rate test. --- .../ieee80211/mode/Ieee80211ModeSet.cc | 40 ++++++++++---- .../ieee80211/mode/Ieee80211ModeSet.h | 8 +++ .../ieee80211/mode/Ieee80211VhtMode.cc | 7 ++- .../ieee80211/mode/Ieee80211VhtMode.h | 2 +- ...e80211ConfiguredResponseRateSelection.test | 13 ++--- .../module/Ieee80211HtGreenfieldRuntime.test | 43 ++++++++------- tests/unit/Ieee80211HtGreenfield_1.test | 54 ++++++++++++++++--- 7 files changed, 113 insertions(+), 54 deletions(-) diff --git a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.cc b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.cc index 7ea1164aa78..9bf49a06b61 100644 --- a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.cc +++ b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.cc @@ -496,9 +496,14 @@ int Ieee80211ModeSet::findModeIndex(const IIeee80211Mode *mode) const for (size_t index = 0; index < entries.size(); index++) if (entries[index].mode == mode) return index; - // HT mixed and Greenfield modes have distinct cached identities because their - // preambles and timing differ, but they represent the same data-rate mode for - // mode-set membership and rate selection purposes. + return -1; +} + +int Ieee80211ModeSet::findEquivalentModeIndex(const IIeee80211Mode *mode) const +{ + int modeIndex = findModeIndex(mode); + if (modeIndex != -1) + return modeIndex; if (auto htMode = dynamic_cast(mode)) { auto htDataMode = htMode->getDataMode(); for (size_t index = 0; index < entries.size(); index++) { @@ -535,7 +540,7 @@ bool Ieee80211ModeSet::getIsMandatory(const IIeee80211Mode *mode) const const IIeee80211Mode *Ieee80211ModeSet::findMode(const IIeee80211Mode *mode) const { - int index = findModeIndex(mode); + int index = findEquivalentModeIndex(mode); return index >= 0 ? entries[index].mode : nullptr; } @@ -650,12 +655,20 @@ const IIeee80211Mode *Ieee80211ModeSet::getFasterMandatoryMode(const IIeee80211M return nullptr; } -const Ieee80211ModeSet *Ieee80211ModeSet::getControlResponseModeSet(const IIeee80211Mode *mode) const +const Ieee80211ModeSet::ControlResponseMode& Ieee80211ModeSet::resolveControlResponseMode(const IIeee80211Mode *mode) const { - // IEEE 802.11 prohibits HT-GF format for control response frames; use a - // same-band HT-mixed profile while retaining the received mode's rate parameters. + auto cachedMode = controlResponseModeCache.find(mode); + if (cachedMode != controlResponseModeCache.end()) + return cachedMode->second; + if (!containsMode(mode)) + throw cRuntimeError("Control response mode is not in operation mode '%s': '%s'", getName(), mode->getName()); + + const Ieee80211ModeSet *controlResponseModeSet = this; + const IIeee80211Mode *controlResponseMode = mode; + // IEEE 802.11-2024, 10.6.6.5.7 requires HT control responses to use the + // HT-mixed format; 19.3.9.5.1 defines HT-mixed and HT-Greenfield preambles. if (auto htMode = dynamic_cast(mode)) { - const Ieee80211ModeSet *controlResponseModeSet = nullptr; + controlResponseModeSet = nullptr; for (size_t index = 0; index < (&modeSets)->size(); index++) { auto candidateModeSet = &(&modeSets)->at(index); auto candidateMode = dynamic_cast(candidateModeSet->findMode(mode)); @@ -670,14 +683,19 @@ const Ieee80211ModeSet *Ieee80211ModeSet::getControlResponseModeSet(const IIeee8 } if (controlResponseModeSet == nullptr) throw cRuntimeError("No same-band HT-mixed control response mode set for mode: '%s'", mode->getName()); - return controlResponseModeSet; + controlResponseMode = controlResponseModeSet->getMode(mode); } - return this; + return controlResponseModeCache.emplace(mode, ControlResponseMode { controlResponseModeSet, controlResponseMode }).first->second; +} + +const Ieee80211ModeSet *Ieee80211ModeSet::getControlResponseModeSet(const IIeee80211Mode *mode) const +{ + return resolveControlResponseMode(mode).modeSet; } const IIeee80211Mode *Ieee80211ModeSet::getControlResponseMode(const IIeee80211Mode *mode) const { - return getControlResponseModeSet(mode)->getMode(mode); + return resolveControlResponseMode(mode).mode; } const Ieee80211ModeSet *Ieee80211ModeSet::findModeSet(const char *mode) diff --git a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h index 19e861d833c..ee23d827671 100644 --- a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h +++ b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h @@ -27,16 +27,24 @@ class INET_API Ieee80211ModeSet : public IPrintableObject, public cObject bool operator()(const Entry& left, const Entry& right) { return left.mode->getDataMode()->getNetBitrate() < right.mode->getDataMode()->getNetBitrate(); } }; + struct ControlResponseMode { + const Ieee80211ModeSet *modeSet; + const IIeee80211Mode *mode; + }; + protected: std::string name; const std::vector entries; + mutable std::map controlResponseModeCache; public: static const DelayedInitializer> modeSets; protected: int findModeIndex(const IIeee80211Mode *mode) const; + int findEquivalentModeIndex(const IIeee80211Mode *mode) const; int getModeIndex(const IIeee80211Mode *mode) const; + const ControlResponseMode& resolveControlResponseMode(const IIeee80211Mode *mode) const; public: Ieee80211ModeSet(const char *name, const std::vector entries); diff --git a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211VhtMode.cc b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211VhtMode.cc index 89b142e37cb..07a6f6c0db4 100644 --- a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211VhtMode.cc +++ b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211VhtMode.cc @@ -674,8 +674,8 @@ const Ieee80211VhtMode *Ieee80211VhtCompliantModes::getCompliantMode(const Ieee8 { const char *name = ""; // TODO unsigned int nss = mcsMode->getNumNss(); - auto htModeId = std::make_tuple(mcsMode->getBandwidth(), mcsMode->getMcsIndex(), guardIntervalType, nss); - auto mode = singleton.modeCache.find(htModeId); + auto vhtModeId = std::make_tuple(mcsMode->getBandwidth(), mcsMode->getMcsIndex(), centerFrequencyMode, preambleFormat, guardIntervalType, nss); + auto mode = singleton.modeCache.find(vhtModeId); if (mode == singleton.modeCache.end()) { const Ieee80211OfdmSignalMode *legacySignal = nullptr; const Ieee80211VhtSignalMode *htSignal = nullptr; @@ -693,7 +693,7 @@ const Ieee80211VhtMode *Ieee80211VhtCompliantModes::getCompliantMode(const Ieee8 const Ieee80211VhtDataMode *dataMode = new Ieee80211VhtDataMode(mcsMode, mcsMode->getBandwidth(), guardIntervalType); const Ieee80211VhtPreambleMode *preambleMode = new Ieee80211VhtPreambleMode(htSignal, legacySignal, preambleFormat, dataMode->getNumberOfSpatialStreams()); const Ieee80211VhtMode *htMode = new Ieee80211VhtMode(name, preambleMode, dataMode, centerFrequencyMode); - singleton.modeCache.insert(std::pair, const Ieee80211VhtMode *>(htModeId, htMode)); + singleton.modeCache.emplace(vhtModeId, htMode); return htMode; } return mode->second; @@ -1072,4 +1072,3 @@ const DI Ieee80211VhtmcsTable::vhtMcs9BW160MHzNss8([](){ return } /* namespace physicallayer */ } /* namespace inet */ - diff --git a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211VhtMode.h b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211VhtMode.h index 531c6e1d48d..c804237e55e 100644 --- a/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211VhtMode.h +++ b/src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211VhtMode.h @@ -679,7 +679,7 @@ class INET_API Ieee80211VhtCompliantModes protected: static OPP_THREAD_LOCAL const Ieee80211VhtCompliantModes singleton; - mutable std::map, const Ieee80211VhtMode *> modeCache; + mutable std::map, const Ieee80211VhtMode *> modeCache; public: Ieee80211VhtCompliantModes(); diff --git a/tests/module/Ieee80211ConfiguredResponseRateSelection.test b/tests/module/Ieee80211ConfiguredResponseRateSelection.test index 02a1ae0c711..7f0d0436e19 100644 --- a/tests/module/Ieee80211ConfiguredResponseRateSelection.test +++ b/tests/module/Ieee80211ConfiguredResponseRateSelection.test @@ -1,7 +1,6 @@ %description: -Checks that configured response rates are cached as local modes and converted -to HT-mixed control-response modes at compute time for DCF and QoS rate -selection, including a Basic BlockAck response. +Checks through the public rate-selection API that configured Greenfield response +rates are converted to HT-mixed modes for DCF and QoS ACK, CTS, and Basic BlockAck responses. %file: Test.cc #include @@ -9,10 +8,8 @@ selection, including a Basic BlockAck response. #include "inet/common/InitStages.h" #include "inet/common/packet/Packet.h" #include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h" -#define protected public // test-only access to verify initialized response-mode ownership #include "inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.h" #include "inet/linklayer/ieee80211/mac/rateselection/RateSelection.h" -#undef protected #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.h" #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h" #include "inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Tag_m.h" @@ -52,12 +49,8 @@ class ConfiguredResponseProbe : public cSimpleModule throw cRuntimeError("Configured response bitrate was not applied to both rate selections"); auto sourceMode = Ieee80211ModeSet::getModeSet("n(greenfield-2.4Ghz)")->getMode(Mbps(6.5)); - if (dcfRateSelection->responseAckFrameMode != sourceMode || dcfRateSelection->responseCtsFrameMode != sourceMode - || qosRateSelection->responseAckFrameMode != sourceMode || qosRateSelection->responseCtsFrameMode != sourceMode - || qosRateSelection->responseBlockAckFrameMode != sourceMode) - throw cRuntimeError("Configured response modes were not cached as local Greenfield modes"); Packet packet("receivedFrame"); - packet.addTag()->setMode(sourceMode); + packet.addTag()->setMode(sourceMode); assertConfiguredResponse("DCF ACK", dcfRateSelection->computeResponseAckFrameMode(nullptr, nullptr)); assertConfiguredResponse("DCF CTS", dcfRateSelection->computeResponseCtsFrameMode(nullptr, nullptr)); diff --git a/tests/module/Ieee80211HtGreenfieldRuntime.test b/tests/module/Ieee80211HtGreenfieldRuntime.test index 1699371f3e0..b1ef7410d76 100644 --- a/tests/module/Ieee80211HtGreenfieldRuntime.test +++ b/tests/module/Ieee80211HtGreenfieldRuntime.test @@ -1,7 +1,7 @@ %description: -Checks during one real packet exchange that Greenfield and mixed HT -operation select their corresponding preamble formats. +Checks real HT-Greenfield transmissions using response-free, group-addressed +traffic from an ad hoc station. %file: Test.cc #include @@ -20,7 +20,6 @@ class HtPreambleCheckingRadio : public Ieee80211Radio, public cListener { protected: int htGreenfieldTransmissionCount = 0; - int htMixedTransmissionCount = 0; virtual void initialize(int stage) override { @@ -39,17 +38,13 @@ class HtPreambleCheckingRadio : public Ieee80211Radio, public cListener return; if (mode->getPreambleMode()->getPreambleFormat() == Ieee80211HtPreambleMode::HT_PREAMBLE_GREENFIELD) htGreenfieldTransmissionCount++; - else if (mode->getPreambleMode()->getPreambleFormat() == Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED) - htMixedTransmissionCount++; } virtual void finish() override { Ieee80211Radio::finish(); if (htGreenfieldTransmissionCount > 0) - std::cout << "Observed " << htGreenfieldTransmissionCount << " runtime HT greenfield transmission(s).\n"; - if (htMixedTransmissionCount > 0) - std::cout << "Observed " << htMixedTransmissionCount << " runtime HT mixed transmission(s).\n"; + std::cout << "Observed " << htGreenfieldTransmissionCount << " runtime HT-Greenfield broadcast transmission(s).\n"; } }; @@ -59,7 +54,9 @@ Define_Module(HtPreambleCheckingRadio); %file: test.ned -import inet.examples.wireless.lan80211.Lan80211; +import inet.networklayer.configurator.ipv4.Ipv4NetworkConfigurator; +import inet.node.inet.AdhocHost; +import inet.physicallayer.wireless.ieee80211.packetlevel.Ieee80211ScalarRadioMedium; import inet.physicallayer.wireless.ieee80211.packetlevel.Ieee80211Radio; module HtPreambleCheckingRadio extends Ieee80211Radio @@ -68,10 +65,14 @@ module HtPreambleCheckingRadio extends Ieee80211Radio @class(HtPreambleCheckingRadio); } -network TestHtGreenfield extends Lan80211 +network TestHtGreenfield { parameters: - numHosts = default(2); + int numHosts = default(2); + submodules: + configurator: Ipv4NetworkConfigurator; + radioMedium: Ieee80211ScalarRadioMedium; + host[numHosts]: AdhocHost; } %inifile: omnetpp.ini @@ -92,20 +93,18 @@ seed-set = 0 abstract = false network = TestHtGreenfield sim-time-limit = 2s -*.numHosts = 2 +*.numHosts = 1 *.host[0].numApps = 1 -*.host[0].app[0].typename = "PingApp" -*.host[0].app[0].destAddr = "host[1]" +*.host[0].app[0].typename = "UdpBasicApp" +*.host[0].app[0].destAddresses = "255.255.255.255" +*.host[0].app[0].destPort = 1000 +*.host[0].app[0].messageLength = 100B +*.host[0].app[0].startTime = 100ms *.host[0].app[0].sendInterval = 100ms -*.host[1].numApps = 1 -*.host[1].app[0].typename = "PingApp" -*.host[1].app[0].destAddr = "host[0]" -*.host[1].app[0].sendInterval = 100ms -*.ap.wlan[*].opMode = "n(mixed-2.4Ghz)" -*.host[1].wlan[*].opMode = "n(mixed-2.4Ghz)" +**.limitedBroadcast = true +**.wlan[*].opMode = "n(greenfield-2.4Ghz)" %extraargs: -c HtPreambleRuntime %contains-regex: stdout -Observed [1-9][0-9]* runtime HT greenfield transmission\(s\). -Observed [1-9][0-9]* runtime HT mixed transmission\(s\). +Observed [1-9][0-9]* runtime HT-Greenfield broadcast transmission\(s\). diff --git a/tests/unit/Ieee80211HtGreenfield_1.test b/tests/unit/Ieee80211HtGreenfield_1.test index c73d94e0502..416dd931431 100644 --- a/tests/unit/Ieee80211HtGreenfield_1.test +++ b/tests/unit/Ieee80211HtGreenfield_1.test @@ -1,14 +1,17 @@ %description: Checks that the 802.11n mixed and Greenfield mode profiles preserve distinct -preamble formats and timing while supporting cross-profile lookup, and that -legacy ERP mode profiles retain strict mode membership. +preamble formats and timing, retain strict membership, and support explicit +cross-profile lookup for control-response translation. Also checks VHT cache identity. %includes: +#include + #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211DsssOfdmMode.h" #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ErpOfdmMode.h" #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.h" #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h" #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211OfdmMode.h" +#include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211VhtMode.h" %global: using namespace inet; @@ -32,12 +35,23 @@ static bool rejectsUnknownMode(const Ieee80211ModeSet *modeSet, const IIeee80211 } } -static bool rejectsControlResponseModeSet(const Ieee80211ModeSet *modeSet, const IIeee80211Mode *mode) +static bool rejectsNonLocalControlResponseMode(const Ieee80211ModeSet *modeSet, const IIeee80211Mode *mode) { try { modeSet->getControlResponseModeSet(mode); return false; } + catch (const cRuntimeError& error) { + return std::strstr(error.what(), "not in operation mode") != nullptr; + } +} + +static bool rejectsMandatoryLookup(const Ieee80211ModeSet *modeSet, const IIeee80211Mode *mode) +{ + try { + modeSet->getIsMandatory(mode); + return false; + } catch (const cRuntimeError&) { return true; } @@ -65,13 +79,18 @@ ASSERT(greenfieldMode->getPreambleMode()->getDuration() == SimTime(24, SIMTIME_U ASSERT(mixedMode->getDuration(B(0)) == SimTime(40, SIMTIME_US)); ASSERT(greenfieldMode->getDuration(B(0)) == SimTime(28, SIMTIME_US)); ASSERT(mixedMode->getDuration(B(0)) - greenfieldMode->getDuration(B(0)) == SimTime(12, SIMTIME_US)); -ASSERT(mixedProfile->containsMode(greenfieldMode)); -ASSERT(greenfieldProfile->containsMode(mixedMode)); +ASSERT(!mixedProfile->containsMode(greenfieldMode)); +ASSERT(!greenfieldProfile->containsMode(mixedMode)); +ASSERT(mixedProfile->getSlowerMode(greenfieldMode) == nullptr); +ASSERT(greenfieldProfile->getFasterMode(mixedMode) == nullptr); +ASSERT(rejectsMandatoryLookup(mixedProfile, greenfieldMode)); +ASSERT(rejectsMandatoryLookup(greenfieldProfile, mixedMode)); ASSERT(mixedProfile->getMode(greenfieldMode) == mixedMode); ASSERT(greenfieldProfile->getMode(mixedMode) == greenfieldMode); ASSERT(mixedProfile->getControlResponseMode(mixedMode) == mixedMode); ASSERT(greenfieldProfile->getControlResponseModeSet(greenfieldMode) == mixedProfile); ASSERT(greenfieldProfile->getControlResponseMode(greenfieldMode) == mixedMode); +ASSERT(greenfieldProfile->getControlResponseMode(greenfieldMode) == mixedMode); auto greenfield5GhzMode = Ieee80211HtCompliantModes::getCompliantMode( &Ieee80211HtmcsTable::htMcs0BW20MHz, @@ -80,7 +99,30 @@ auto greenfield5GhzMode = Ieee80211HtCompliantModes::getCompliantMode( Ieee80211HtModeBase::HT_GUARD_INTERVAL_LONG); ASSERT(greenfield5GhzMode->getCenterFrequencyMode() == Ieee80211HtMode::BAND_5GHZ); ASSERT(greenfield5GhzMode->getPreambleMode()->getPreambleFormat() == Ieee80211HtPreambleMode::HT_PREAMBLE_GREENFIELD); -ASSERT(rejectsControlResponseModeSet(greenfieldProfile, greenfield5GhzMode)); +ASSERT(!greenfieldProfile->containsMode(greenfield5GhzMode)); +ASSERT(rejectsNonLocalControlResponseMode(greenfieldProfile, greenfield5GhzMode)); + +auto vhtMixed5GhzMode = Ieee80211VhtCompliantModes::getCompliantMode( + &Ieee80211VhtmcsTable::vhtMcs0BW20MHzNss1, + Ieee80211VhtMode::BAND_5GHZ, + Ieee80211VhtPreambleMode::HT_PREAMBLE_MIXED, + Ieee80211VhtModeBase::HT_GUARD_INTERVAL_LONG); +auto vhtGreenfield5GhzMode = Ieee80211VhtCompliantModes::getCompliantMode( + &Ieee80211VhtmcsTable::vhtMcs0BW20MHzNss1, + Ieee80211VhtMode::BAND_5GHZ, + Ieee80211VhtPreambleMode::HT_PREAMBLE_GREENFIELD, + Ieee80211VhtModeBase::HT_GUARD_INTERVAL_LONG); +auto vhtMixed2GhzMode = Ieee80211VhtCompliantModes::getCompliantMode( + &Ieee80211VhtmcsTable::vhtMcs0BW20MHzNss1, + Ieee80211VhtMode::BAND_2_4GHZ, + Ieee80211VhtPreambleMode::HT_PREAMBLE_MIXED, + Ieee80211VhtModeBase::HT_GUARD_INTERVAL_LONG); +ASSERT(vhtMixed5GhzMode != vhtGreenfield5GhzMode); +ASSERT(vhtMixed5GhzMode != vhtMixed2GhzMode); +ASSERT(vhtGreenfield5GhzMode != vhtMixed2GhzMode); +ASSERT(vhtMixed5GhzMode->getCenterFrequencyMode() == Ieee80211VhtMode::BAND_5GHZ); +ASSERT(vhtGreenfield5GhzMode->getPreambleMode()->getPreambleFormat() == Ieee80211VhtPreambleMode::HT_PREAMBLE_GREENFIELD); +ASSERT(vhtMixed2GhzMode->getCenterFrequencyMode() == Ieee80211VhtMode::BAND_2_4GHZ); auto erpMixedProfile = Ieee80211ModeSet::getModeSet("g(mixed)"); auto erpOnlyProfile = Ieee80211ModeSet::getModeSet("g(erp)");