A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
wifi-he-network.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2016 SEBASTIEN DERONNE
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Sebastien Deronne <sebastien.deronne@gmail.com>
7 */
8
9#include "ns3/attribute-container.h"
10#include "ns3/boolean.h"
11#include "ns3/command-line.h"
12#include "ns3/config.h"
13#include "ns3/double.h"
14#include "ns3/enum.h"
15#include "ns3/he-phy.h"
16#include "ns3/internet-stack-helper.h"
17#include "ns3/ipv4-address-helper.h"
18#include "ns3/ipv4-global-routing-helper.h"
19#include "ns3/log.h"
20#include "ns3/mobility-helper.h"
21#include "ns3/multi-model-spectrum-channel.h"
22#include "ns3/on-off-helper.h"
23#include "ns3/packet-sink-helper.h"
24#include "ns3/packet-sink.h"
25#include "ns3/spectrum-wifi-helper.h"
26#include "ns3/ssid.h"
27#include "ns3/string.h"
28#include "ns3/udp-client-server-helper.h"
29#include "ns3/udp-server.h"
30#include "ns3/uinteger.h"
31#include "ns3/wifi-acknowledgment.h"
32#include "ns3/yans-wifi-channel.h"
33#include "ns3/yans-wifi-helper.h"
34
35#include <algorithm>
36#include <functional>
37
38// This is a simple example in order to show how to configure an IEEE 802.11ax Wi-Fi network.
39//
40// It outputs the UDP or TCP goodput for every HE MCS value, which depends on the MCS value (0 to
41// 11), the channel width (20, 40, 80 or 160 MHz) and the guard interval (800ns, 1600ns or 3200ns).
42// The PHY bitrate is constant over all the simulation run. The user can also specify the distance
43// between the access point and the station: the larger the distance the smaller the goodput.
44//
45// The simulation assumes a configurable number of stations in an infrastructure network:
46//
47// STA AP
48// * *
49// | |
50// n1 n2
51//
52// Packets in this simulation belong to BestEffort Access Class (AC_BE).
53// By selecting an acknowledgment sequence for DL MU PPDUs, it is possible to aggregate a
54// Round Robin scheduler to the AP, so that DL MU PPDUs are sent by the AP via DL OFDMA.
55
56using namespace ns3;
57
58NS_LOG_COMPONENT_DEFINE("he-wifi-network");
59
60int
61main(int argc, char* argv[])
62{
63 bool udp{true};
64 bool downlink{true};
65 bool useRts{false};
66 bool use80Plus80{false};
67 bool useExtendedBlockAck{false};
68 Time simulationTime{"10s"};
69 meter_u distance{1.0};
70 double frequency{5}; // whether 2.4, 5 or 6 GHz
71 std::size_t nStations{1};
72 std::string dlAckSeqType{"NO-OFDMA"};
73 bool enableUlOfdma{false};
74 bool enableBsrp{false};
75 std::string mcsStr;
76 std::vector<uint64_t> mcsValues;
77 int channelWidth{-1}; // in MHz, -1 indicates an unset value
78 int guardInterval{-1}; // in nanoseconds, -1 indicates an unset value
79 uint32_t payloadSize =
80 700; // must fit in the max TX duration when transmitting at MCS 0 over an RU of 26 tones
81 std::string phyModel{"Yans"};
82 double minExpectedThroughput{0.0};
83 double maxExpectedThroughput{0.0};
85
87 cmd.AddValue("frequency",
88 "Whether working in the 2.4, 5 or 6 GHz band (other values gets rejected)",
89 frequency);
90 cmd.AddValue("distance",
91 "Distance in meters between the station and the access point",
92 distance);
93 cmd.AddValue("simulationTime", "Simulation time", simulationTime);
94 cmd.AddValue("udp", "UDP if set to 1, TCP otherwise", udp);
95 cmd.AddValue("downlink",
96 "Generate downlink flows if set to 1, uplink flows otherwise",
97 downlink);
98 cmd.AddValue("useRts", "Enable/disable RTS/CTS", useRts);
99 cmd.AddValue("use80Plus80", "Enable/disable use of 80+80 MHz", use80Plus80);
100 cmd.AddValue("useExtendedBlockAck", "Enable/disable use of extended BACK", useExtendedBlockAck);
101 cmd.AddValue("nStations", "Number of non-AP HE stations", nStations);
102 cmd.AddValue("dlAckType",
103 "Ack sequence type for DL OFDMA (NO-OFDMA, ACK-SU-FORMAT, MU-BAR, AGGR-MU-BAR)",
105 cmd.AddValue("enableUlOfdma",
106 "Enable UL OFDMA (useful if DL OFDMA is enabled and TCP is used)",
108 cmd.AddValue("enableBsrp",
109 "Enable BSRP (useful if DL and UL OFDMA are enabled and TCP is used)",
110 enableBsrp);
111 cmd.AddValue(
112 "muSchedAccessReqInterval",
113 "Duration of the interval between two requests for channel access made by the MU scheduler",
115 cmd.AddValue(
116 "mcs",
117 "list of comma separated MCS values to test; if unset, all MCS values (0-11) are tested",
118 mcsStr);
119 cmd.AddValue("channelWidth",
120 "if set, limit testing to a specific channel width expressed in MHz (20, 40, 80 "
121 "or 160 MHz)",
122 channelWidth);
123 cmd.AddValue("guardInterval",
124 "if set, limit testing to a specific guard interval duration expressed in "
125 "nanoseconds (800, 1600 or 3200 ns)",
127 cmd.AddValue("payloadSize", "The application payload size in bytes", payloadSize);
128 cmd.AddValue("phyModel",
129 "PHY model to use when OFDMA is disabled (Yans or Spectrum). If 80+80 MHz or "
130 "OFDMA is enabled "
131 "then Spectrum is automatically selected",
132 phyModel);
133 cmd.AddValue("minExpectedThroughput",
134 "if set, simulation fails if the lowest throughput is below this value",
136 cmd.AddValue("maxExpectedThroughput",
137 "if set, simulation fails if the highest throughput is above this value",
139 cmd.Parse(argc, argv);
140
141 if (useRts)
142 {
143 Config::SetDefault("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue("0"));
144 Config::SetDefault("ns3::WifiDefaultProtectionManager::EnableMuRts", BooleanValue(true));
145 }
146
147 if (dlAckSeqType == "ACK-SU-FORMAT")
148 {
149 Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
151 }
152 else if (dlAckSeqType == "MU-BAR")
153 {
154 Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
156 }
157 else if (dlAckSeqType == "AGGR-MU-BAR")
158 {
159 Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
161 }
162 else if (dlAckSeqType != "NO-OFDMA")
163 {
164 NS_ABORT_MSG("Invalid DL ack sequence type (must be NO-OFDMA, ACK-SU-FORMAT, MU-BAR or "
165 "AGGR-MU-BAR)");
166 }
167
168 if (phyModel != "Yans" && phyModel != "Spectrum")
169 {
170 NS_ABORT_MSG("Invalid PHY model (must be Yans or Spectrum)");
171 }
172 if (use80Plus80 || (dlAckSeqType != "NO-OFDMA"))
173 {
174 // SpectrumWifiPhy is required for 80+80 MHz and OFDMA
175 phyModel = "Spectrum";
176 }
177
178 double prevThroughput[12] = {0};
179
180 std::cout << "MCS value"
181 << "\t\t"
182 << "Channel width"
183 << "\t\t"
184 << "GI"
185 << "\t\t\t"
186 << "Throughput" << '\n';
187 uint8_t minMcs = 0;
188 uint8_t maxMcs = 11;
189
190 if (mcsStr.empty())
191 {
192 for (uint8_t mcs = minMcs; mcs <= maxMcs; ++mcs)
193 {
194 mcsValues.push_back(mcs);
195 }
196 }
197 else
198 {
199 AttributeContainerValue<UintegerValue, ',', std::vector> attr;
201 checker->SetItemChecker(MakeUintegerChecker<uint8_t>());
202 attr.DeserializeFromString(mcsStr, checker);
203 mcsValues = attr.Get();
204 std::sort(mcsValues.begin(), mcsValues.end());
205 }
206
207 int minChannelWidth = 20;
208 int maxChannelWidth = frequency == 2.4 ? 40 : 160;
209 if (channelWidth >= minChannelWidth && channelWidth <= maxChannelWidth)
210 {
211 minChannelWidth = channelWidth;
212 maxChannelWidth = channelWidth;
213 }
214 int minGi = enableUlOfdma ? 1600 : 800;
215 int maxGi = 3200;
216 if (guardInterval >= minGi && guardInterval <= maxGi)
217 {
218 minGi = guardInterval;
220 }
221
222 for (const auto mcs : mcsValues)
223 {
224 uint8_t index = 0;
225 double previous = 0;
226 for (int width = minChannelWidth; width <= maxChannelWidth; width *= 2) // MHz
227 {
228 const auto is80Plus80 = (use80Plus80 && (width == 160));
229 const std::string widthStr = is80Plus80 ? "80+80" : std::to_string(width);
230 const auto segmentWidthStr = is80Plus80 ? "80" : widthStr;
231 for (int gi = maxGi; gi >= minGi; gi /= 2) // Nanoseconds
232 {
233 if (!udp)
234 {
235 Config::SetDefault("ns3::TcpSocket::SegmentSize", UintegerValue(payloadSize));
236 }
237
239 wifiStaNodes.Create(nStations);
241 wifiApNode.Create(1);
242
247 std::string channelStr("{0, " + segmentWidthStr + ", ");
250
251 std::ostringstream ossDataMode;
252 ossDataMode << "HeMcs" << mcs;
253
254 if (frequency == 6)
255 {
257 channelStr += "BAND_6GHZ, 0}";
258 Config::SetDefault("ns3::LogDistancePropagationLossModel::ReferenceLoss",
259 DoubleValue(48));
260 }
261 else if (frequency == 5)
262 {
263 std::ostringstream ossControlMode;
264 ossControlMode << "OfdmRate" << nonHtRefRateMbps << "Mbps";
266 channelStr += "BAND_5GHZ, 0}";
267 }
268 else if (frequency == 2.4)
269 {
270 std::ostringstream ossControlMode;
271 ossControlMode << "ErpOfdmRate" << nonHtRefRateMbps << "Mbps";
273 channelStr += "BAND_2_4GHZ, 0}";
274 Config::SetDefault("ns3::LogDistancePropagationLossModel::ReferenceLoss",
275 DoubleValue(40));
276 }
277 else
278 {
279 NS_FATAL_ERROR("Wrong frequency value!");
280 }
281
282 if (is80Plus80)
283 {
284 channelStr += std::string(";") + channelStr;
285 }
286
287 wifi.SetStandard(WIFI_STANDARD_80211ax);
288 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
289 "DataMode",
291 "ControlMode",
292 ctrlRate);
293 // Set guard interval
294 wifi.ConfigHeOptions("GuardInterval", TimeValue(NanoSeconds(gi)));
295
296 Ssid ssid = Ssid("ns3-80211ax");
297
298 if (phyModel == "Spectrum")
299 {
301
303 spectrumChannel->AddPropagationLossModel(lossModel);
304
306 phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
307 phy.SetChannel(spectrumChannel);
308
309 mac.SetType("ns3::StaWifiMac",
310 "Ssid",
311 SsidValue(ssid),
312 "MpduBufferSize",
314 phy.Set("ChannelSettings", StringValue(channelStr));
315 staDevices = wifi.Install(phy, mac, wifiStaNodes);
316
317 if (dlAckSeqType != "NO-OFDMA")
318 {
319 mac.SetMultiUserScheduler("ns3::RrMultiUserScheduler",
320 "EnableUlOfdma",
322 "EnableBsrp",
324 "AccessReqInterval",
326 }
327 mac.SetType("ns3::ApWifiMac",
328 "EnableBeaconJitter",
329 BooleanValue(false),
330 "Ssid",
331 SsidValue(ssid));
332 apDevice = wifi.Install(phy, mac, wifiApNode);
333 }
334 else
335 {
338 phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
339 phy.SetChannel(channel.Create());
340
341 mac.SetType("ns3::StaWifiMac",
342 "Ssid",
343 SsidValue(ssid),
344 "MpduBufferSize",
346 phy.Set("ChannelSettings", StringValue(channelStr));
347 staDevices = wifi.Install(phy, mac, wifiStaNodes);
348
349 mac.SetType("ns3::ApWifiMac",
350 "EnableBeaconJitter",
351 BooleanValue(false),
352 "Ssid",
353 SsidValue(ssid));
354 apDevice = wifi.Install(phy, mac, wifiApNode);
355 }
356
357 int64_t streamNumber = 150;
360
361 // mobility.
364
365 positionAlloc->Add(Vector(0.0, 0.0, 0.0));
366 positionAlloc->Add(Vector(distance, 0.0, 0.0));
367 mobility.SetPositionAllocator(positionAlloc);
368
369 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
370
371 mobility.Install(wifiApNode);
372 mobility.Install(wifiStaNodes);
373
374 /* Internet stack*/
376 stack.Install(wifiApNode);
377 stack.Install(wifiStaNodes);
378 streamNumber += stack.AssignStreams(wifiApNode, streamNumber);
379 streamNumber += stack.AssignStreams(wifiStaNodes, streamNumber);
380
382 address.SetBase("192.168.1.0", "255.255.255.0");
385
386 staNodeInterfaces = address.Assign(staDevices);
388
389 /* Setting applications */
390 ApplicationContainer serverApp;
391 auto serverNodes = downlink ? std::ref(wifiStaNodes) : std::ref(wifiApNode);
394 for (std::size_t i = 0; i < nStations; i++)
395 {
396 serverInterfaces.Add(downlink ? staNodeInterfaces.Get(i)
397 : apNodeInterface.Get(0));
398 clientNodes.Add(downlink ? wifiApNode.Get(0) : wifiStaNodes.Get(i));
399 }
400
401 const auto maxLoad =
402 HePhy::GetDataRate(mcs, MHz_u{static_cast<double>(width)}, NanoSeconds(gi), 1) /
403 nStations;
404 if (udp)
405 {
406 // UDP flow
407 uint16_t port = 9;
409 serverApp = server.Install(serverNodes.get());
410 streamNumber += server.AssignStreams(serverNodes.get(), streamNumber);
411
412 serverApp.Start(Seconds(0));
413 serverApp.Stop(simulationTime + Seconds(1));
414 const auto packetInterval = payloadSize * 8.0 / maxLoad;
415
416 for (std::size_t i = 0; i < nStations; i++)
417 {
419 client.SetAttribute("MaxPackets", UintegerValue(4294967295U));
420 client.SetAttribute("Interval", TimeValue(Seconds(packetInterval)));
421 client.SetAttribute("PacketSize", UintegerValue(payloadSize));
422 ApplicationContainer clientApp = client.Install(clientNodes.Get(i));
423 streamNumber += client.AssignStreams(clientNodes.Get(i), streamNumber);
424
425 clientApp.Start(Seconds(1));
426 clientApp.Stop(simulationTime + Seconds(1));
427 }
428 }
429 else
430 {
431 // TCP flow
432 uint16_t port = 50000;
434 PacketSinkHelper packetSinkHelper("ns3::TcpSocketFactory", localAddress);
435 serverApp = packetSinkHelper.Install(serverNodes.get());
436 streamNumber += packetSinkHelper.AssignStreams(serverNodes.get(), streamNumber);
437
438 serverApp.Start(Seconds(0));
439 serverApp.Stop(simulationTime + Seconds(1));
440
441 for (std::size_t i = 0; i < nStations; i++)
442 {
443 OnOffHelper onoff("ns3::TcpSocketFactory", Ipv4Address::GetAny());
444 onoff.SetAttribute("OnTime",
445 StringValue("ns3::ConstantRandomVariable[Constant=1]"));
446 onoff.SetAttribute("OffTime",
447 StringValue("ns3::ConstantRandomVariable[Constant=0]"));
448 onoff.SetAttribute("PacketSize", UintegerValue(payloadSize));
449 onoff.SetAttribute("DataRate", DataRateValue(maxLoad));
452 onoff.SetAttribute("Remote", remoteAddress);
453 ApplicationContainer clientApp = onoff.Install(clientNodes.Get(i));
454 streamNumber += onoff.AssignStreams(clientNodes.Get(i), streamNumber);
455
456 clientApp.Start(Seconds(1));
457 clientApp.Stop(simulationTime + Seconds(1));
458 }
459 }
460
462
463 Simulator::Stop(simulationTime + Seconds(1));
465
466 // When multiple stations are used, there are chances that association requests
467 // collide and hence the throughput may be lower than expected. Therefore, we relax
468 // the check that the throughput cannot decrease by introducing a scaling factor (or
469 // tolerance)
470 auto tolerance = 0.10;
471 auto rxBytes = 0.0;
472 if (udp)
473 {
474 for (uint32_t i = 0; i < serverApp.GetN(); i++)
475 {
476 rxBytes +=
477 payloadSize * DynamicCast<UdpServer>(serverApp.Get(i))->GetReceived();
478 }
479 }
480 else
481 {
482 for (uint32_t i = 0; i < serverApp.GetN(); i++)
483 {
484 rxBytes += DynamicCast<PacketSink>(serverApp.Get(i))->GetTotalRx();
485 }
486 }
487 auto throughput = (rxBytes * 8) / simulationTime.GetMicroSeconds(); // Mbit/s
488
490
491 std::cout << +mcs << "\t\t\t" << widthStr << " MHz\t\t"
492 << (widthStr.size() > 3 ? "" : "\t") << gi << " ns\t\t\t" << throughput
493 << " Mbit/s" << std::endl;
494
495 // test first element
496 if (mcs == minMcs && width == 20 && gi == 3200)
497 {
499 {
500 NS_LOG_ERROR("Obtained throughput " << throughput << " is not expected!");
501 exit(1);
502 }
503 }
504 // test last element
505 if (mcs == maxMcs && width == maxChannelWidth && gi == 800)
506 {
507 if (maxExpectedThroughput > 0 &&
509 {
510 NS_LOG_ERROR("Obtained throughput " << throughput << " is not expected!");
511 exit(1);
512 }
513 }
514 // Skip comparisons with previous cases if more than one stations are present
515 // because, e.g., random collisions in the establishment of Block Ack agreements
516 // have an impact on throughput
517 if (nStations == 1)
518 {
519 // test previous throughput is smaller (for the same mcs)
520 if (throughput * (1 + tolerance) > previous)
521 {
523 }
524 else if (throughput > 0)
525 {
526 NS_LOG_ERROR("Obtained throughput " << throughput << " is not expected!");
527 exit(1);
528 }
529 // test previous throughput is smaller (for the same channel width and GI)
530 if (throughput * (1 + tolerance) > prevThroughput[index])
531 {
532 prevThroughput[index] = throughput;
533 }
534 else if (throughput > 0)
535 {
536 NS_LOG_ERROR("Obtained throughput " << throughput << " is not expected!");
537 exit(1);
538 }
539 }
540 index++;
541 }
542 }
543 }
544 return 0;
545}
a polymophic address class
Definition address.h:90
AttributeValue implementation for Address.
Definition address.h:275
holds a vector of ns3::Application pointers.
void Start(Time start) const
Start all of the Applications in this container at the start time given as a parameter.
Ptr< Application > Get(uint32_t i) const
Get the Ptr<Application> stored in this container at a given index.
void Stop(Time stop) const
Arrange for all of the Applications in this container to Stop() at the Time given as a parameter.
uint32_t GetN() const
Get the number of Ptr<Application> stored in this container.
A container for one type of attribute.
AttributeValue implementation for Boolean.
Definition boolean.h:26
Parse command-line arguments.
AttributeValue implementation for DataRate.
Definition data-rate.h:285
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition double.h:31
Hold variables of type enum.
Definition enum.h:52
static uint64_t GetDataRate(uint8_t mcsValue, MHz_u channelWidth, Time guardInterval, uint8_t nss)
Return the data rate corresponding to the supplied HE MCS index, channel width, guard interval,...
Definition he-phy.cc:1708
static uint64_t GetNonHtReferenceRate(uint8_t mcsValue)
Calculate the rate in bps of the non-HT Reference Rate corresponding to the supplied HE MCS index.
Definition he-phy.cc:1750
an Inet address class
aggregate IP/TCP/UDP functionality to existing Nodes.
A helper class to make life easier while doing simple IPv4 address assignment in scripts.
static Ipv4Address GetAny()
static void PopulateRoutingTables()
Build a routing database and initialize the routing tables of the nodes in the simulation.
holds a vector of std::pair of Ptr<Ipv4> and interface index.
Helper class used to assign positions and mobility models to nodes.
holds a vector of ns3::NetDevice pointers
keep track of a set of node pointers.
A helper to make it easier to instantiate an ns3::OnOffApplication on a set of nodes.
A helper to make it easier to instantiate an ns3::PacketSinkApplication on a set of nodes.
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:66
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:560
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition simulator.cc:131
static void Run()
Run the simulation.
Definition simulator.cc:167
static void Stop()
Tell the Simulator the calling event should be the last one executed.
Definition simulator.cc:175
Make it easy to create and manage PHY objects for the spectrum model.
The IEEE 802.11 SSID Information Element.
Definition ssid.h:25
AttributeValue implementation for Ssid.
Definition ssid.h:85
Hold variables of type string.
Definition string.h:45
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
AttributeValue implementation for Time.
Definition nstime.h:1431
Create a client application which sends UDP packets carrying a 32bit sequence number and a 64 bit tim...
Create a server application which waits for input UDP packets and uses the information carried into t...
Hold an unsigned integer type.
Definition uinteger.h:34
helps to create WifiNetDevice objects
static int64_t AssignStreams(NetDeviceContainer c, int64_t stream)
Assign a fixed random variable stream number to the random variables used by the PHY and MAC aspects ...
create MAC layers for a ns3::WifiNetDevice.
@ DLT_IEEE802_11_RADIO
Include Radiotap link layer information.
static YansWifiChannelHelper Default()
Create a channel helper in a default working state.
Make it easy to create and manage PHY objects for the YANS model.
uint16_t port
Definition dsdv-manet.cc:33
Ptr< AttributeChecker > MakeAttributeContainerChecker()
Make uninitialized AttributeContainerChecker using explicit types.
void SetDefault(std::string name, const AttributeValue &value)
Definition config.cc:883
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#define NS_LOG_ERROR(msg)
Use NS_LOG to output a message of level LOG_ERROR.
Definition log.h:243
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:436
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1380
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1344
@ WIFI_STANDARD_80211ax
address
Definition first.py:36
stack
Definition first.py:33
Every class exported by the ns3 library is enclosed in the ns3 namespace.
STL namespace.
staDevices
Definition third.py:87
ssid
Definition third.py:82
channel
Definition third.py:77
mac
Definition third.py:81
wifi
Definition third.py:84
wifiApNode
Definition third.py:75
mobility
Definition third.py:92
wifiStaNodes
Definition third.py:73
phy
Definition third.py:78
std::ofstream throughput