diff --git a/CHANGES.md b/CHANGES.md index 619896f6b..284cda094 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -32,6 +32,7 @@ This file is a best-effort approach to solving this issue; we will do our best b * (docs) Models documentation format guidelines have been updated. * (zigbee) Adjust pedantic link cost requirement in ``NeighborTable::LookUpForBestParent``, a minimum link cost of 3 is not required now. +* (wifi) Normal Ack, BlockAck and BlockAckReq frames are transmitted, if appropriate, as non-HT duplicate PPDUs on a bandwidth matching that of the data frame transmitted in the same frame exchange sequence. ## Changes from ns-3.43 to ns-3.44 diff --git a/src/wifi/model/wifi-remote-station-manager.cc b/src/wifi/model/wifi-remote-station-manager.cc index e8b0578d8..5cfd38991 100644 --- a/src/wifi/model/wifi-remote-station-manager.cc +++ b/src/wifi/model/wifi-remote-station-manager.cc @@ -702,6 +702,32 @@ WifiRemoteStationManager::GetCtsToSelfTxVector() false); } +void +WifiRemoteStationManager::AdjustTxVectorForCtlResponse(WifiTxVector& txVector, + MHz_u allowedWidth) const +{ + NS_LOG_FUNCTION(this << txVector << allowedWidth); + + auto modulation = txVector.GetModulationClass(); + + if (allowedWidth >= 40 && + (modulation == WIFI_MOD_CLASS_DSSS || modulation == WIFI_MOD_CLASS_HR_DSSS)) + { + // control frame must be sent in a non-HT duplicate PPDU because it must protect a frame + // being transmitted on at least 40 MHz. Change the modulation class to ERP-OFDM and the + // rate to 6 Mbps + txVector.SetMode(ErpOfdmPhy::GetErpOfdmRate6Mbps()); + modulation = txVector.GetModulationClass(); + } + // do not set allowedWidth as the TX width if the modulation class is (HR-)DSSS (allowedWidth + // may be 20 MHz) or allowedWidth is 22 MHz (the selected modulation class may be OFDM) + if (modulation != WIFI_MOD_CLASS_DSSS && modulation != WIFI_MOD_CLASS_HR_DSSS && + allowedWidth != 22) + { + txVector.SetChannelWidth(allowedWidth); + } +} + WifiTxVector WifiRemoteStationManager::GetRtsTxVector(Mac48Address address, MHz_u allowedWidth) { @@ -724,24 +750,8 @@ WifiRemoteStationManager::GetRtsTxVector(Mac48Address address, MHz_u allowedWidt { v = DoGetRtsTxVector(Lookup(address)); } - auto modulation = v.GetModulationClass(); - if (allowedWidth >= 40 && - (modulation == WIFI_MOD_CLASS_DSSS || modulation == WIFI_MOD_CLASS_HR_DSSS)) - { - // RTS must be sent in a non-HT duplicate PPDU because it must protect a frame being - // transmitted on at least 40 MHz. Change the modulation class to ERP-OFDM and the rate - // to 6 Mbps - v.SetMode(ErpOfdmPhy::GetErpOfdmRate6Mbps()); - modulation = v.GetModulationClass(); - } - // do not set allowedWidth as the TX width if the modulation class is (HR-)DSSS (allowedWidth - // may be >= 40 MHz) or allowedWidth is 22 MHz (the selected modulation class may be OFDM) - if (modulation != WIFI_MOD_CLASS_DSSS && modulation != WIFI_MOD_CLASS_HR_DSSS && - allowedWidth != 22) - { - v.SetChannelWidth(allowedWidth); - } + AdjustTxVectorForCtlResponse(v, allowedWidth); return v; } @@ -810,6 +820,9 @@ WifiRemoteStationManager::GetAckTxVector(Mac48Address to, const WifiTxVector& da v.SetChannelWidth(m_wifiPhy->GetTxBandwidth(ackMode)); v.SetGuardInterval(GetGuardIntervalForMode(ackMode, m_wifiPhy->GetDevice())); v.SetNss(1); + + AdjustTxVectorForCtlResponse(v, dataTxVector.GetChannelWidth()); + return v; } @@ -827,6 +840,9 @@ WifiRemoteStationManager::GetBlockAckTxVector(Mac48Address to, v.SetChannelWidth(m_wifiPhy->GetTxBandwidth(blockAckMode)); v.SetGuardInterval(GetGuardIntervalForMode(blockAckMode, m_wifiPhy->GetDevice())); v.SetNss(1); + + AdjustTxVectorForCtlResponse(v, dataTxVector.GetChannelWidth()); + return v; } diff --git a/src/wifi/model/wifi-remote-station-manager.h b/src/wifi/model/wifi-remote-station-manager.h index 6c12f98bf..1135eea84 100644 --- a/src/wifi/model/wifi-remote-station-manager.h +++ b/src/wifi/model/wifi-remote-station-manager.h @@ -889,6 +889,16 @@ class WifiRemoteStationManager : public Object * transmission of the data packet itself. */ WifiTxVector GetCtsToSelfTxVector(); + /** + * Adjust the TXVECTOR for a control response frame to ensure that, if appropriate, the non-HT + * duplicate format is used and the TX width matches that of the data frame transmitted (in case + * of, e.g., Ack or BlockAck) or to transmit (in case of, e.g., RTS or BlockAckReq) in the same + * frame exchange sequence. + * + * @param txVector the TXVECTOR to adjust + * @param allowedWidth the allowed width for the data frame in the same frame exchange sequence + */ + void AdjustTxVectorForCtlResponse(WifiTxVector& txVector, MHz_u allowedWidth) const; /** * Adjust the TXVECTOR for an initial Control frame to ensure that the modulation class * is non-HT and the rate is 6 Mbps, 12 Mbps or 24 Mbps. diff --git a/src/wifi/test/wifi-retransmit-test.cc b/src/wifi/test/wifi-retransmit-test.cc index 1d01e9875..e90671cf2 100644 --- a/src/wifi/test/wifi-retransmit-test.cc +++ b/src/wifi/test/wifi-retransmit-test.cc @@ -66,6 +66,11 @@ NS_LOG_COMPONENT_DEFINE("WifiRetransmitTest"); * * In case of multi-link devices, the first TXOP is carried out on link 0 and the second TXOP on * link 1. It is checked that QSRC and CW are updated on the link on which the TXOP is carried out. + * + * The TX width of transmitted frames is also checked, considering that the AP operates on a + * 160 MHz channel and the non-AP STA does not support 160 MHz operations (hence the AP will use + * its primary80 channel to transmit to the non-AP STA). In case of multi-link devices, the second + * link is operated on a 40 MHz channel. */ class WifiRetransmitTest : public TestCase { @@ -190,12 +195,12 @@ WifiRetransmitTest::DoSetup() SpectrumWifiPhyHelper phy(m_nLinks); phy.SetChannel(CreateObject()); - // use default 20 MHz channel in 5 GHz band - phy.Set(0, "ChannelSettings", StringValue("{0, 20, BAND_5GHZ, 0}")); + // use default 80 MHz channel in 5 GHz band for the non-AP STA + phy.Set(0, "ChannelSettings", StringValue("{0, 80, BAND_5GHZ, 0}")); if (m_nLinks > 1) { - // use default 20 MHz channel in 6 GHz band - phy.Set(1, "ChannelSettings", StringValue("{0, 20, BAND_6GHZ, 0}")); + // use default 40 MHz channel in 6 GHz band + phy.Set(1, "ChannelSettings", StringValue("{0, 40, BAND_6GHZ, 0}")); } Config::SetDefault("ns3::WifiRemoteStationManager::RtsCtsThreshold", @@ -209,6 +214,7 @@ WifiRetransmitTest::DoSetup() WifiHelper wifi; wifi.SetStandard(m_nLinks == 1 ? WIFI_STANDARD_80211ax : WIFI_STANDARD_80211be); + wifi.ConfigVhtOptions("Support160MHzOperation", BooleanValue(false)); wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager", "DataMode", WifiModeValue(HePhy::GetHeMcs8()), @@ -221,6 +227,10 @@ WifiRetransmitTest::DoSetup() NetDeviceContainer staDevice = wifi.Install(phy, mac, wifiStaNode); m_staMac = StaticCast(StaticCast(staDevice.Get(0))->GetMac()); + wifi.ConfigVhtOptions("Support160MHzOperation", BooleanValue(true)); + // use default 160 MHz channel in 5 GHz band for the AP + phy.Set(0, "ChannelSettings", StringValue("{0, 160, BAND_5GHZ, 0}")); + mac.SetType("ns3::ApWifiMac"); mac.SetEdca(AC_BE, "TxopLimits", @@ -383,6 +393,16 @@ WifiRetransmitTest::Transmit(uint8_t phyId, return; } + // check width of transmitted frames, except CTS because we do not support yet static/dynamic + // bandwidth operation + const auto expectedWidth = (phyId == 0 ? 80 : 40); + if (!hdr.IsCts()) + { + NS_TEST_EXPECT_MSG_EQ(txVector.GetChannelWidth(), + expectedWidth, + "Unexpected width for " << hdr.GetTypeString()); + } + if (m_eventIt != m_events.cend()) { // check that the expected frame is being transmitted @@ -654,7 +674,7 @@ WifiRetransmitTest::DoRun() m_staMac->GetDevice()->GetNode(), GetApplication(2, m_pktSize)); - Simulator::Stop(Seconds(1)); + Simulator::Stop(Seconds(0.550)); Simulator::Run(); NS_TEST_EXPECT_MSG_EQ((m_eventIt == m_events.cend()), true, "Not all events took place"); diff --git a/src/wifi/test/wifi-test.cc b/src/wifi/test/wifi-test.cc index 94949222c..240f426e9 100644 --- a/src/wifi/test/wifi-test.cc +++ b/src/wifi/test/wifi-test.cc @@ -1508,7 +1508,7 @@ Bug2222TestCase::DoRun() * * The scenario considers a UDP transmission between a 40 MHz 802.11ac station and a * 40 MHz 802.11ac access point. All transmission parameters are checked so as - * to ensure that only 2 {starting frequency, channelWidth, Number of subbands + * to ensure that only 3 {starting frequency, channelWidth, Number of subbands * in SpectrumModel, modulation type} tuples are used. * * See \bugid{2843} @@ -1688,33 +1688,48 @@ Bug2843TestCase::DoRun() // {starting frequency, channelWidth, Number of subbands in SpectrumModel, modulation type} // tuples std::size_t numberTuples = m_distinctTuples.size(); - NS_TEST_ASSERT_MSG_EQ(numberTuples, 2, "Only two distinct tuples expected"); - NS_TEST_ASSERT_MSG_EQ(std::get<0>(m_distinctTuples[0]) - Hz_u{20e6}, + NS_TEST_ASSERT_MSG_EQ(numberTuples, 3, "Only three distinct tuples expected"); + NS_TEST_EXPECT_MSG_EQ(std::get<0>(m_distinctTuples[0]) - Hz_u{20e6}, std::get<0>(m_distinctTuples[1]), "The starting frequency of the first tuple should be shifted 20 MHz to " "the right wrt second tuple"); // Note that the first tuple should the one initiated by the beacon, i.e. non-HT OFDM (20 MHz) - NS_TEST_ASSERT_MSG_EQ(std::get<1>(m_distinctTuples[0]), + NS_TEST_EXPECT_MSG_EQ(std::get<1>(m_distinctTuples[0]), MHz_u{20}, "First tuple's channel width should be 20 MHz"); - NS_TEST_ASSERT_MSG_EQ(std::get<2>(m_distinctTuples[0]), + NS_TEST_EXPECT_MSG_EQ(std::get<2>(m_distinctTuples[0]), 193, "First tuple should have 193 subbands (64+DC, 20MHz+DC, inband and 64*2 " "out-of-band, 20MHz on each side)"); - NS_TEST_ASSERT_MSG_EQ(std::get<3>(m_distinctTuples[0]), + NS_TEST_EXPECT_MSG_EQ(std::get<3>(m_distinctTuples[0]), WifiModulationClass::WIFI_MOD_CLASS_OFDM, "First tuple should be OFDM"); - // Second tuple - NS_TEST_ASSERT_MSG_EQ(std::get<1>(m_distinctTuples[1]), + // Second tuple: data frames, VHT (40 MHz) + NS_TEST_EXPECT_MSG_EQ(std::get<1>(m_distinctTuples[1]), channelWidth, "Second tuple's channel width should be 40 MHz"); - NS_TEST_ASSERT_MSG_EQ(std::get<2>(m_distinctTuples[1]), + NS_TEST_EXPECT_MSG_EQ(std::get<2>(m_distinctTuples[1]), 385, "Second tuple should have 385 subbands (128+DC, 40MHz+DC, inband and " "128*2 out-of-band, 40MHz on each side)"); - NS_TEST_ASSERT_MSG_EQ(std::get<3>(m_distinctTuples[1]), + NS_TEST_EXPECT_MSG_EQ(std::get<3>(m_distinctTuples[1]), WifiModulationClass::WIFI_MOD_CLASS_VHT, "Second tuple should be VHT_OFDM"); + // Third tuple: control response frames, non-HT (OFDM) duplicate (40 MHz) + NS_TEST_EXPECT_MSG_EQ(std::get<0>(m_distinctTuples[1]), + std::get<0>(m_distinctTuples[2]), + "The starting frequency of the third tuple should be the same as the " + "second tuple"); + NS_TEST_EXPECT_MSG_EQ(std::get<1>(m_distinctTuples[2]), + channelWidth, + "Third tuple's channel width should be 40 MHz"); + NS_TEST_EXPECT_MSG_EQ(std::get<2>(m_distinctTuples[2]), + 385, + "Third tuple should have 385 subbands (128+DC, 40MHz+DC, inband and " + "128*2 out-of-band, 40MHz on each side)"); + NS_TEST_EXPECT_MSG_EQ(std::get<3>(m_distinctTuples[2]), + WifiModulationClass::WIFI_MOD_CLASS_OFDM, + "Third tuple should be OFDM"); } //-----------------------------------------------------------------------------