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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions WHATSNEW
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ Notable backward incompatible changes are the following:
adaptive rate control, either where a station transmits to several peers or
where it sends group-addressed traffic.

2. IEEE 802.11 PHY mode API

External implementations of IIeee80211DataMode must implement the new pure
virtual getGuardInterval() query. Return the modeled guard interval as a
simtime_t, or -1 when the PHY has no guard interval. The bitrate-based
Ieee80211ModeSet getMode() and findMode() overloads also take a trailing,
defaulted guardInterval argument. Ordinary existing calls still compile;
member-function pointers must use the new signature. See the migration guide.

Notable backward compatible changes are the following:

1. IEEE 802.11 per-station rate statistics
Expand Down Expand Up @@ -67,6 +76,20 @@ Notable backward compatible changes are the following:
default. Serializers written against an earlier INET version need no source
change. See the migration guide for the new signatures.

5. IEEE 802.11 HT/VHT guard intervals and timing

HT MCS 32, 73, and 76 definitions are corrected. HT/VHT signal fields use
long-GI symbol timing independently of the data GI. Mixed-format HT and VHT
short-GI data airtimes are rounded to 4 us boundaries; HT greenfield retains
its unrounded short-GI airtime. Results and fingerprints may change for
simulations using the affected modes and frame formats.

RateSelection and QosRateSelection now accept dataFrameGuardInterval to
qualify a configured dataFrameBitrate. The default, -1s, leaves the GI
unspecified; an explicit value must match the selected mode's modeled GI.
Mode-set listeners also coordinate runtime mode changes with MAC,
management, and rate selection state.


INET-4.7 (July 2026) — feature release
--------------------------------------
Expand Down
26 changes: 26 additions & 0 deletions doc/src/migration-guide/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,32 @@ Migrating Code from INET 3.x
============================
Release: |release|

Migrating IEEE 802.11 PHY Modes
------------------------------

External implementations of ``IIeee80211DataMode`` must now implement the pure
virtual guard-interval query:

.. code-block:: c++

const simtime_t getGuardInterval() const override;

Return the modeled guard interval in simulation time units. For a PHY without a
guard interval, use an explicit override returning ``-1``. FHSS, DSSS, HR-DSSS,
and IR use this value; OFDM, HT, and VHT return their modeled interval.

The bitrate-based ``Ieee80211ModeSet::getMode()`` and ``findMode()`` overloads
now take a trailing ``simtime_t guardInterval = -1`` argument. Existing ordinary
calls can omit it. Update member-function pointer declarations to include this
argument and supply it when invoking through a pointer. Rebuild external code
against the changed interface.

``Ieee80211Mac``, ``Ieee80211MgmtBase``, ``RateSelection``, and
``ModeSetListener`` implement ``IIeee80211ModeSetListener``. External subclasses
inherit this interface; adding the base does not itself require a source change.
If a subclass replaces mode-set notification handling, preserve the inherited
state updates required by its base class.

Migrating ``FieldsChunkSerializer`` Subclasses
---------------------------------------------

Expand Down
48 changes: 34 additions & 14 deletions src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,24 +67,12 @@ void Ieee80211Mac::initialize(int stage)
radioModule->subscribe(IRadio::receptionStateChangedSignal, this);
radioModule->subscribe(IRadio::transmissionStateChangedSignal, this);
radioModule->subscribe(IRadio::receivedSignalPartChangedSignal, this);
getContainingNicModule(this)->subscribe(modesetChangedSignal, this);
radio = check_and_cast<IRadio *>(radioModule);
ds = check_and_cast<IDs *>(getSubmodule("ds"));
rx = check_and_cast<IRx *>(getSubmodule("rx"));
tx = check_and_cast<ITx *>(getSubmodule("tx"));
int operationalHtSpatialStreamLimit = std::min(radio->getAntenna()->getNumAntennas(),
modeSet->getMaximumNumberOfSpatialStreams());
std::set<Hz> operationalChannelWidths;
if (modeSet->isHtOperationSupported()) {
const auto *transmitter = dynamic_cast<const Ieee80211Transmitter *>(radio->getTransmitter());
const auto *receiver = dynamic_cast<const Ieee80211Receiver *>(radio->getReceiver());
if (transmitter == nullptr || receiver == nullptr)
throw cRuntimeError("HT operation requires Ieee80211Transmitter and Ieee80211Receiver");
for (auto channelWidth : modeSet->getHtSupportedChannelWidths())
if (transmitter->isHtChannelWidthSupported(channelWidth) &&
receiver->isHtChannelWidthSupported(channelWidth))
operationalChannelWidths.insert(channelWidth);
}
mib->updateLocalHtCapabilities(modeSet, operationalChannelWidths, operationalHtSpatialStreamLimit);
updateLocalHtCapabilities();
emit(modesetChangedSignal, modeSet);
if (isUp())
initializeRadioMode();
Expand All @@ -97,6 +85,24 @@ void Ieee80211Mac::initialize(int stage)
}
}

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

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

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

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

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

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

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

virtual void receiveSignal(cComponent *source, simsignal_t signalID, intval_t value, cObject *details) override;
virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details) override;
using MacProtocolBase::receiveSignal;
virtual void configureRadioMode(physicallayer::IRadio::RadioMode radioMode);
virtual void configureNetworkInterface() override;
Expand Down
12 changes: 8 additions & 4 deletions src/inet/linklayer/ieee80211/mac/channelaccess/Dcaf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,15 @@ void Dcaf::expectedChannelAccess(simtime_t time)
void Dcaf::receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details)
{
Enter_Method("%s", cComponent::getSignalName(signalID));
if (signalID == modesetChangedSignal && obj != modeSet)
applyModeSet(check_and_cast<physicallayer::Ieee80211ModeSet *>(obj));
}

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

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

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

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

Expand Down
12 changes: 8 additions & 4 deletions src/inet/linklayer/ieee80211/mac/channelaccess/Edcaf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,15 @@ int Edcaf::getCwMin(AccessCategory ac, int aCwMin)
void Edcaf::receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details)
{
Enter_Method("%s", cComponent::getSignalName(signalID));
if (signalID == modesetChangedSignal && obj != modeSet)
applyModeSet(check_and_cast<physicallayer::Ieee80211ModeSet *>(obj));
}

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

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

protected:
IContention *contention = nullptr;
IChannelAccess::ICallback *callback = nullptr;
Expand Down
9 changes: 7 additions & 2 deletions src/inet/linklayer/ieee80211/mac/common/ModeSetListener.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@ void ModeSetListener::initialize(int stage)
void ModeSetListener::receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj, cObject *details)
{
Enter_Method("%s", cComponent::getSignalName(signalID));
if (signalID == modesetChangedSignal && obj != modeSet)
applyModeSet(check_and_cast<physicallayer::Ieee80211ModeSet *>(obj));
}

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

} /* namespace ieee80211 */
Expand Down
7 changes: 6 additions & 1 deletion src/inet/linklayer/ieee80211/mac/common/ModeSetListener.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@

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

namespace inet {
namespace ieee80211 {

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

protected:
physicallayer::Ieee80211ModeSet *modeSet = nullptr;

Expand Down
14 changes: 10 additions & 4 deletions src/inet/linklayer/ieee80211/mac/ratecontrol/RateControlBase.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,16 @@ void RateControlBase::receiveSignal(cComponent *source, simsignal_t signalID, cO
{
Enter_Method("%s", cComponent::getSignalName(signalID));

if (signalID == modesetChangedSignal) {
modeSet = check_and_cast<Ieee80211ModeSet *>(obj);
resetRateControl();
}
if (signalID == modesetChangedSignal && obj != modeSet)
applyModeSet(check_and_cast<Ieee80211ModeSet *>(obj));
}

void RateControlBase::applyModeSet(const Ieee80211ModeSet *newModeSet)
{
Enter_Method_Silent();
modeSet = const_cast<Ieee80211ModeSet *>(newModeSet);
getInitialMode(); // Validate fixed initial rates even before the first peer is used.
resetRateControl();
}

} /* namespace ieee80211 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace ieee80211 {
class INET_API RateControlBase : public ModeSetListener, public IRateControl
{
public:
virtual void applyModeSet(const physicallayer::Ieee80211ModeSet *modeSet) override;
static simsignal_t datarateChangedSignal;

protected:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@ namespace ieee80211 {

using namespace inet::physicallayer;

const IIeee80211Mode *selectGroupAddressedMode(const Ieee80211ModeSet *modeSet, const IIeee80211Mode *requestedMode)
{
// IEEE Std 802.11-2024, 10.6.5.1 and 10.6.5.4. The model advertises
// mandatory legacy operational modes as its BSS basic legacy rate set.
const IIeee80211Mode *legacyMode = nullptr;
for (const auto *candidate : modeSet->getLegacyOperationalModes()) {
if (!modeSet->getIsMandatory(candidate))
continue;
if (candidate == requestedMode)
return candidate;
if (legacyMode == nullptr || candidate->getDataMode()->getNetBitrate() > legacyMode->getDataMode()->getNetBitrate())
legacyMode = candidate;
}
return legacyMode != nullptr ? legacyMode : requestedMode;
}

namespace {

static const IIeee80211Mode *getLegacyFallback(const Ieee80211ModeSet *modeSet,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
namespace inet {
namespace ieee80211 {

INET_API const physicallayer::IIeee80211Mode *selectGroupAddressedMode(
const physicallayer::Ieee80211ModeSet *modeSet, const physicallayer::IIeee80211Mode *requestedMode);

/**
* Selects a mode that is compatible with the negotiated receive capabilities
* of a peer. Non-HT modes are returned unchanged. A null peer state denotes
Expand Down
Loading