539 lines
20 KiB
C++
539 lines
20 KiB
C++
/*
|
|
* Copyright (c) 2023 DERONNE SOFTWARE ENGINEERING
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
|
*
|
|
* Author: Sébastien Deronne <sebastien.deronne@gmail.com>
|
|
*/
|
|
|
|
#include "ns3/boolean.h"
|
|
#include "ns3/command-line.h"
|
|
#include "ns3/config.h"
|
|
#include "ns3/data-rate.h"
|
|
#include "ns3/double.h"
|
|
#include "ns3/enum.h"
|
|
#include "ns3/error-model.h"
|
|
#include "ns3/inet-socket-address.h"
|
|
#include "ns3/internet-stack-helper.h"
|
|
#include "ns3/ipv4-address-helper.h"
|
|
#include "ns3/ipv4-l3-protocol.h"
|
|
#include "ns3/ipv4-list-routing-helper.h"
|
|
#include "ns3/ipv4-static-routing-helper.h"
|
|
#include "ns3/ipv4-static-routing.h"
|
|
#include "ns3/log.h"
|
|
#include "ns3/mobility-helper.h"
|
|
#include "ns3/mobility-model.h"
|
|
#include "ns3/names.h"
|
|
#include "ns3/node.h"
|
|
#include "ns3/on-off-helper.h"
|
|
#include "ns3/packet-sink-helper.h"
|
|
#include "ns3/packet-sink.h"
|
|
#include "ns3/simulator.h"
|
|
#include "ns3/socket.h"
|
|
#include "ns3/ssid.h"
|
|
#include "ns3/string.h"
|
|
#include "ns3/test.h"
|
|
#include "ns3/trace-helper.h"
|
|
#include "ns3/udp-socket-factory.h"
|
|
#include "ns3/udp-socket.h"
|
|
#include "ns3/uinteger.h"
|
|
#include "ns3/wifi-mac.h"
|
|
#include "ns3/wifi-net-device.h"
|
|
#include "ns3/wifi-psdu.h"
|
|
#include "ns3/yans-wifi-channel.h"
|
|
#include "ns3/yans-wifi-helper.h"
|
|
|
|
#include <limits>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
// This is an example to verify performance of 802.11aa groupcast with retries (GCR) in comparison
|
|
// with the usual NoAck/NoRetry retransmission policy. In the latter, a groupcast frame is
|
|
// transmitted only once, whereas GCR allows to retransmit groupcast frames to improve reliability.
|
|
//
|
|
// The simulation considers a single 802.11ax AP and a configurable number of GCR-capable STAs in an
|
|
// infrastructure network. Multicast traffic is generated from the AP to all the non-AP STAs and
|
|
// artificial errors can be introduced to mimic interference on the wireless channel.
|
|
//
|
|
// There are a number of command-line options available to control the scenario under test. The list
|
|
// of available command-line options can be listed with the following command:
|
|
// ./ns3 run "wifi-multicast --help"
|
|
//
|
|
// The main command-line options are:
|
|
// --gcrRetransmissionPolicy: control the retransmission policy by selecting "NoAckNoRetry" for
|
|
// no retransmission, "GcrUr" for GCR with unsolicited retries or "GcrBlockAck" for GCR Block Ack
|
|
// --nStations: control the number of GCR-capable STAs associated to the AP
|
|
// --accessCategory: control the access category to use for the multicast traffic
|
|
// --multicastFrameErrorRate: set the artificial frame error rate for the groupcast traffic
|
|
// --nRetriesGcrUr: if GCR-UR is selected, this parameter controls the maximum number of retries
|
|
// --gcrProtection: select the protection mechanism for groupcast frames if GCR-UR or GCR-BA is
|
|
// used, either "Rts-Cts" or "Cts-To-Self" can be selected
|
|
//
|
|
// Example usage for NoAckNoRetry and a frame error rate of 20%:
|
|
// ./ns3 run "wifi-multicast --gcrRetransmissionPolicy=NoAckNoRetry --multicastFrameErrorRate=0.2"
|
|
// which outputs:
|
|
// Node TX packets TX bytes RX packets RX bytes Throughput (Mbit/s)
|
|
// AP 10 10000 0 0 11.1111
|
|
// STA1 0 0 10 10000 10.992
|
|
//
|
|
// Example usage for GCR-UR with up to 2 retries and the same frame error rate:
|
|
// ./ns3 run "wifi-multicast --gcrRetransmissionPolicy=GcrUr --nRetriesGcrUr=2
|
|
// --multicastFrameErrorRate=0.2"
|
|
// which outputs:
|
|
// Node TX packets TX bytes RX packets RX bytes Throughput (Mbit/s)
|
|
// AP 10 10000 0 0 11.1111
|
|
// STA1 0 0 10 10000 10.992
|
|
//
|
|
// Example usage for GCR-BA with 4 STAs and the same frame error rate:
|
|
// ./ns3 run "wifi-multicast --gcrRetransmissionPolicy=GcrBlockAck --nStations=4"
|
|
// which outputs:
|
|
// Node TX packets TX bytes RX packets RX bytes Throughput (Mbit/s)
|
|
// AP 10 10000 0 0 11.1111
|
|
// STA1 0 0 10 10000 8.26959
|
|
// STA2 0 0 10 10000 8.26959
|
|
// STA3 0 0 10 10000 8.26959
|
|
// STA4 0 0 10 10000 8.26959
|
|
|
|
using namespace ns3;
|
|
|
|
NS_LOG_COMPONENT_DEFINE("WifiMulticast");
|
|
|
|
uint64_t g_txBytes; //!< Number of generated bytes
|
|
Time g_firstTx; //!< Time at which first TX packet is generated
|
|
Time g_lastTx; //!< Time at which last TX packet is generated
|
|
Time g_lastRx; //!< Time at which last RX packet is received
|
|
|
|
/**
|
|
* Parse context strings of the form "/NodeList/x/DeviceList/x/..." to extract the NodeId integer
|
|
*
|
|
* @param context The context to parse.
|
|
* @return the NodeId
|
|
*/
|
|
uint32_t
|
|
ContextToNodeId(const std::string& context)
|
|
{
|
|
std::string sub = context.substr(10);
|
|
uint32_t pos = sub.find("/Device");
|
|
return std::stoi(sub.substr(0, pos));
|
|
}
|
|
|
|
/**
|
|
* Socket sent packet.
|
|
*
|
|
* @param context The context.
|
|
* @param p The packet.
|
|
*/
|
|
void
|
|
SocketTxPacket(std::string context, Ptr<const Packet> p)
|
|
{
|
|
if (g_txBytes == 0)
|
|
{
|
|
g_firstTx = Simulator::Now();
|
|
}
|
|
g_txBytes += p->GetSize();
|
|
g_lastTx = Simulator::Now();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param context The context.
|
|
* @param p The packet.
|
|
* @param from sender address.
|
|
*/
|
|
void
|
|
SocketRxPacket(std::string context, Ptr<const Packet> p, const Address& from)
|
|
{
|
|
g_lastRx = Simulator::Now();
|
|
}
|
|
|
|
/**
|
|
* Callback when a frame is transmitted.
|
|
* @param rxErrorModel the post reception error model on the receiver
|
|
* @param ranVar the random variable to determine whether the packet shall be corrupted
|
|
* @param errorRate the configured corruption error rate for multicast frames
|
|
* @param context the context
|
|
* @param psduMap the PSDU map
|
|
* @param txVector the TX vector
|
|
* @param txPowerW the tx power in Watts
|
|
*/
|
|
void
|
|
TxCallback(Ptr<ListErrorModel> rxErrorModel,
|
|
Ptr<RandomVariableStream> ranVar,
|
|
double errorRate,
|
|
std::string context,
|
|
WifiConstPsduMap psduMap,
|
|
WifiTxVector txVector,
|
|
double txPowerW)
|
|
{
|
|
auto psdu = psduMap.begin()->second;
|
|
if (psdu->GetHeader(0).GetAddr1().IsGroup() && !psdu->GetHeader(0).GetAddr1().IsBroadcast() &&
|
|
psdu->GetHeader(0).IsQosData())
|
|
{
|
|
NS_LOG_INFO("AP tx multicast: PSDU=" << *psdu << " TXVECTOR=" << txVector);
|
|
if (ranVar->GetValue() < errorRate)
|
|
{
|
|
auto uid = psdu->GetPayload(0)->GetUid();
|
|
NS_LOG_INFO("Corrupt multicast frame with UID=" << uid);
|
|
rxErrorModel->SetList({uid});
|
|
}
|
|
else
|
|
{
|
|
rxErrorModel->SetList({});
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback when a frame is successfully received.
|
|
*
|
|
* @param context the context
|
|
* @param p the packet
|
|
* @param snr the SNR (in linear scale)
|
|
* @param mode the mode used to transmit the packet
|
|
* @param preamble the preamble
|
|
*/
|
|
void
|
|
RxCallback(std::string context,
|
|
Ptr<const Packet> p,
|
|
double snr,
|
|
WifiMode mode,
|
|
WifiPreamble preamble)
|
|
{
|
|
Ptr<Packet> packet = p->Copy();
|
|
WifiMacHeader hdr;
|
|
packet->RemoveHeader(hdr);
|
|
if (hdr.GetAddr1().IsGroup() && !hdr.GetAddr1().IsBroadcast() && hdr.IsQosData())
|
|
{
|
|
std::stringstream ss;
|
|
hdr.Print(ss);
|
|
NS_LOG_INFO("STA" << ContextToNodeId(context) << " rx multicast: " << ss.str());
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char* argv[])
|
|
{
|
|
bool logging{false};
|
|
bool verbose{false};
|
|
bool pcap{false};
|
|
std::size_t nStations{1};
|
|
Time simulationTime{"10s"};
|
|
uint32_t payloadSize{1000}; // bytes
|
|
DataRate dataRate{"10Mb/s"};
|
|
uint32_t maxPackets{10};
|
|
uint32_t rtsThreshold{std::numeric_limits<uint16_t>::max()};
|
|
std::string targetAddr{"239.192.100.1"};
|
|
std::string accessCategory{"AC_BE"};
|
|
std::string gcrRetransmissionPolicy{"NoAckNoRetry"};
|
|
std::string rateManager{"Constant"};
|
|
uint16_t constantRateMcs{11};
|
|
uint16_t nRetriesGcrUr{7};
|
|
std::string gcrProtection{"Rts-Cts"};
|
|
double multicastFrameErrorRate{0.0};
|
|
uint16_t maxAmpduLength{0};
|
|
double minExpectedPackets{0};
|
|
double maxExpectedPackets{0};
|
|
double minExpectedThroughput{0};
|
|
double maxExpectedThroughput{0};
|
|
double tolerance{0.01};
|
|
|
|
CommandLine cmd(__FILE__);
|
|
cmd.AddValue("logging", "turn on example log components", logging);
|
|
cmd.AddValue("verbose", "turn on all wifi log components", verbose);
|
|
cmd.AddValue("pcap", "turn on pcap file output", pcap);
|
|
cmd.AddValue("nStations", "number of non-AP stations", nStations);
|
|
cmd.AddValue("simulationTime", "Simulation time", simulationTime);
|
|
cmd.AddValue("payloadSize", "The application payload size in bytes", payloadSize);
|
|
cmd.AddValue(
|
|
"maxPackets",
|
|
"The maximum number of packets to be generated by the application (0 for no limit)",
|
|
maxPackets);
|
|
cmd.AddValue("dataRate", "The application data rate", dataRate);
|
|
cmd.AddValue("rtsThreshold", "RTS threshold", rtsThreshold);
|
|
cmd.AddValue("rateManager",
|
|
"The rate adaptation manager to use (Constant, Ideal, MinstrelHt)",
|
|
rateManager);
|
|
cmd.AddValue("mcs",
|
|
"The MCS to use if Constant rate adaptation manager is used",
|
|
constantRateMcs);
|
|
cmd.AddValue("targetAddress", "multicast target address", targetAddr);
|
|
cmd.AddValue("accessCategory",
|
|
"select the multicast traffic access category (AC_BE, AC_BK, AC_VI, AC_VO)",
|
|
accessCategory);
|
|
cmd.AddValue(
|
|
"gcrRetransmissionPolicy",
|
|
"GCR retransmission policy for groupcast frames (NoAckNoRetry, GcrUr, GcrBlockAck)",
|
|
gcrRetransmissionPolicy);
|
|
cmd.AddValue("nRetriesGcrUr",
|
|
"number of retries per groupcast frame when GCR-UR retransmission policy is used",
|
|
nRetriesGcrUr);
|
|
cmd.AddValue("gcrProtection",
|
|
"protection to use for GCR (Rts-Cts or Cts-To-Self)",
|
|
gcrProtection);
|
|
cmd.AddValue("multicastFrameErrorRate",
|
|
"artificial error rate for multicast frame",
|
|
multicastFrameErrorRate);
|
|
cmd.AddValue("maxAmpduLength", "maximum length in bytes of an A-MPDU", maxAmpduLength);
|
|
cmd.AddValue("minExpectedPackets",
|
|
"if set, simulation fails if the lowest amount of received packets is below this "
|
|
"value (in Mbit/s)",
|
|
minExpectedPackets);
|
|
cmd.AddValue("maxExpectedPackets",
|
|
"if set, simulation fails if the highest amount of received packets is above this "
|
|
"value (in Mbit/s)",
|
|
maxExpectedPackets);
|
|
cmd.AddValue("minExpectedThroughput",
|
|
"if set, simulation fails if the throughput is below this value",
|
|
minExpectedThroughput);
|
|
cmd.AddValue("maxExpectedThroughput",
|
|
"if set, simulation fails if the throughput is above this value",
|
|
maxExpectedThroughput);
|
|
cmd.Parse(argc, argv);
|
|
|
|
Config::SetDefault("ns3::WifiMac::RobustAVStreamingSupported", BooleanValue(true));
|
|
|
|
// create nodes
|
|
NodeContainer wifiApNode;
|
|
wifiApNode.Create(1);
|
|
NodeContainer wifiStaNodes;
|
|
wifiStaNodes.Create(nStations);
|
|
|
|
// configure PHY and MAC
|
|
WifiHelper wifi;
|
|
if (verbose)
|
|
{
|
|
WifiHelper::EnableLogComponents();
|
|
}
|
|
if (logging)
|
|
{
|
|
LogComponentEnable("WifiMulticast", LOG_LEVEL_ALL);
|
|
}
|
|
wifi.SetStandard(WIFI_STANDARD_80211ax);
|
|
|
|
YansWifiPhyHelper wifiPhy;
|
|
wifiPhy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
|
|
|
|
auto wifiChannel = YansWifiChannelHelper::Default();
|
|
wifiChannel.SetPropagationDelay("ns3::ConstantSpeedPropagationDelayModel");
|
|
wifiPhy.SetChannel(wifiChannel.Create());
|
|
|
|
WifiMacHelper wifiMac;
|
|
if (rateManager == "Constant")
|
|
{
|
|
wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
|
|
"DataMode",
|
|
StringValue("HeMcs" + std::to_string(constantRateMcs)),
|
|
"RtsCtsThreshold",
|
|
UintegerValue(rtsThreshold));
|
|
}
|
|
else
|
|
{
|
|
wifi.SetRemoteStationManager("ns3::" + rateManager + "RateWifiManager",
|
|
"RtsCtsThreshold",
|
|
UintegerValue(rtsThreshold));
|
|
}
|
|
|
|
if (gcrRetransmissionPolicy != "NoAckNoRetry")
|
|
{
|
|
std::string retransmissionPolicyStr;
|
|
if (gcrRetransmissionPolicy == "GcrUr")
|
|
{
|
|
retransmissionPolicyStr = "GCR_UR";
|
|
}
|
|
else if (gcrRetransmissionPolicy == "GcrBlockAck")
|
|
{
|
|
retransmissionPolicyStr = "GCR_BA";
|
|
}
|
|
else
|
|
{
|
|
std::cout << "Wrong retransmission policy!" << std::endl;
|
|
return 0;
|
|
}
|
|
wifiMac.SetGcrManager("ns3::WifiDefaultGcrManager",
|
|
"RetransmissionPolicy",
|
|
StringValue(retransmissionPolicyStr),
|
|
"UnsolicitedRetryLimit",
|
|
UintegerValue(nRetriesGcrUr),
|
|
"GcrProtectionMode",
|
|
StringValue(gcrProtection));
|
|
}
|
|
|
|
Ssid ssid = Ssid("wifi-multicast");
|
|
|
|
// setup AP
|
|
wifiMac.SetType("ns3::ApWifiMac", "Ssid", SsidValue(ssid));
|
|
auto apDevice = wifi.Install(wifiPhy, wifiMac, wifiApNode);
|
|
|
|
// setup STAs
|
|
wifiMac.SetType("ns3::StaWifiMac", "Ssid", SsidValue(ssid));
|
|
auto staDevices = wifi.Install(wifiPhy, wifiMac, wifiStaNodes);
|
|
|
|
auto rxErrorModel = CreateObject<ListErrorModel>();
|
|
auto ranVar = CreateObject<UniformRandomVariable>();
|
|
ranVar->SetStream(1);
|
|
Config::Connect("/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phys/0/PhyTxPsduBegin",
|
|
MakeCallback(TxCallback).Bind(rxErrorModel, ranVar, multicastFrameErrorRate));
|
|
for (std::size_t i = 0; i < nStations; ++i)
|
|
{
|
|
auto wifiMac = DynamicCast<WifiNetDevice>(staDevices.Get(i))->GetMac();
|
|
wifiMac->GetWifiPhy(0)->SetPostReceptionErrorModel(rxErrorModel);
|
|
}
|
|
Config::Connect("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/$ns3::WifiPhy/State/RxOk",
|
|
MakeCallback(RxCallback));
|
|
|
|
// mobility
|
|
MobilityHelper mobility;
|
|
Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
|
|
positionAlloc->Add(Vector(0.0, 0.0, 0.0));
|
|
for (std::size_t i = 0; i < nStations; ++i)
|
|
{
|
|
positionAlloc->Add(Vector(i, 0.0, 0.0));
|
|
}
|
|
mobility.SetPositionAllocator(positionAlloc);
|
|
mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
|
|
mobility.Install(wifiApNode);
|
|
mobility.Install(wifiStaNodes);
|
|
|
|
// setup static routes to facilitate multicast flood
|
|
Ipv4ListRoutingHelper listRouting;
|
|
Ipv4StaticRoutingHelper staticRouting;
|
|
listRouting.Add(staticRouting, 0);
|
|
|
|
// configure IP stack
|
|
InternetStackHelper internet;
|
|
internet.SetIpv6StackInstall(false);
|
|
internet.SetIpv4ArpJitter(true);
|
|
internet.SetRoutingHelper(listRouting);
|
|
internet.Install(wifiApNode);
|
|
internet.Install(wifiStaNodes);
|
|
|
|
Ipv4AddressHelper ipv4address;
|
|
ipv4address.SetBase("10.0.0.0", "255.255.255.0");
|
|
auto apNodeInterface = ipv4address.Assign(apDevice);
|
|
auto staNodeInterfaces = ipv4address.Assign(staDevices);
|
|
|
|
// add static route in AP
|
|
auto ipv4 = wifiApNode.Get(0)->GetObject<Ipv4>();
|
|
auto routing = staticRouting.GetStaticRouting(ipv4);
|
|
routing->AddHostRouteTo(targetAddr.c_str(),
|
|
ipv4->GetInterfaceForDevice(wifiApNode.Get(0)->GetDevice(0)),
|
|
0);
|
|
|
|
uint8_t tosValue;
|
|
std::string maxAmpduSizeAttribute;
|
|
if (accessCategory == "AC_BE")
|
|
{
|
|
tosValue = 0x70;
|
|
maxAmpduSizeAttribute = "BE_MaxAmpduSize";
|
|
}
|
|
else if (accessCategory == "AC_BK")
|
|
{
|
|
tosValue = 0x28;
|
|
maxAmpduSizeAttribute = "BK_MaxAmpduSize";
|
|
}
|
|
else if (accessCategory == "AC_VI")
|
|
{
|
|
tosValue = 0xb8;
|
|
maxAmpduSizeAttribute = "VI_MaxAmpduSize";
|
|
}
|
|
else if (accessCategory == "AC_VO")
|
|
{
|
|
tosValue = 0xc0;
|
|
maxAmpduSizeAttribute = "VO_MaxAmpduSize";
|
|
}
|
|
else
|
|
{
|
|
NS_ABORT_MSG("Invalid access category");
|
|
}
|
|
auto apWifiMac = DynamicCast<WifiNetDevice>(apDevice.Get(0))->GetMac();
|
|
apWifiMac->SetAttribute(maxAmpduSizeAttribute, UintegerValue(maxAmpduLength));
|
|
|
|
// sinks
|
|
InetSocketAddress sinkSocket(Ipv4Address::GetAny(), 90);
|
|
PacketSinkHelper sinkHelper("ns3::UdpSocketFactory", sinkSocket);
|
|
auto sinks = sinkHelper.Install(wifiStaNodes);
|
|
sinks.Start(Seconds(0));
|
|
sinks.Stop(simulationTime + Seconds(2.0));
|
|
|
|
// source
|
|
InetSocketAddress sourceSocket(targetAddr.c_str(), 90);
|
|
OnOffHelper onoffHelper("ns3::UdpSocketFactory", sourceSocket);
|
|
onoffHelper.SetAttribute("DataRate", DataRateValue(dataRate));
|
|
onoffHelper.SetAttribute("PacketSize", UintegerValue(payloadSize));
|
|
onoffHelper.SetAttribute("MaxBytes", UintegerValue(maxPackets * payloadSize));
|
|
onoffHelper.SetAttribute("Tos", UintegerValue(tosValue));
|
|
onoffHelper.SetAttribute("OnTime", StringValue("ns3::ConstantRandomVariable[Constant=1]"));
|
|
onoffHelper.SetAttribute("OffTime", StringValue("ns3::ConstantRandomVariable[Constant=0]"));
|
|
auto source = onoffHelper.Install(wifiApNode);
|
|
source.Start(Seconds(1));
|
|
source.Stop(simulationTime + Seconds(1.0));
|
|
|
|
// pcap
|
|
if (pcap)
|
|
{
|
|
wifiPhy.EnablePcap("wifi-multicast-AP", apDevice.Get(0));
|
|
for (std::size_t i = 0; i < nStations; ++i)
|
|
{
|
|
wifiPhy.EnablePcap("wifi-multicast-STA" + std::to_string(i + 1), staDevices.Get(i));
|
|
}
|
|
}
|
|
|
|
g_txBytes = 0;
|
|
|
|
Config::Connect("/NodeList/*/$ns3::Node/ApplicationList/*/$ns3::OnOffApplication/Tx",
|
|
MakeCallback(&SocketTxPacket));
|
|
|
|
Config::Connect("/NodeList/*/ApplicationList/*/$ns3::PacketSink/Rx",
|
|
MakeCallback(&SocketRxPacket));
|
|
|
|
// run simulation
|
|
Simulator::Stop(simulationTime + Seconds(2.0));
|
|
Simulator::Run();
|
|
|
|
// check results
|
|
std::cout << "Node\t\t\tTX packets\t\tTX bytes\t\tRX packets\t\tRX bytes\t\tThroughput (Mbit/s)"
|
|
<< std::endl;
|
|
const auto txRate =
|
|
(g_lastTx - g_firstTx).IsStrictlyPositive()
|
|
? static_cast<double>(g_txBytes * 8) / ((g_lastTx - g_firstTx).GetMicroSeconds())
|
|
: 0.0; // Mbit/s
|
|
const auto txPackets = g_txBytes / payloadSize;
|
|
std::cout << "AP"
|
|
<< "\t\t\t" << txPackets << "\t\t\t" << g_txBytes << "\t\t\t0\t\t\t0\t\t\t" << txRate
|
|
<< "" << std::endl;
|
|
for (std::size_t i = 0; i < nStations; ++i)
|
|
{
|
|
const auto rxBytes = sinks.Get(0)->GetObject<PacketSink>()->GetTotalRx();
|
|
const auto rxPackets = rxBytes / payloadSize;
|
|
const auto throughput =
|
|
(g_lastRx - g_firstTx).IsStrictlyPositive()
|
|
? static_cast<double>(rxBytes * 8) / ((g_lastRx - g_firstTx).GetMicroSeconds())
|
|
: 0.0; // Mbit/s
|
|
std::cout << "STA" << i + 1 << "\t\t\t0\t\t\t0\t\t\t" << rxPackets << "\t\t\t" << rxBytes
|
|
<< "\t\t\t" << throughput << "" << std::endl;
|
|
if (rxPackets < minExpectedPackets)
|
|
{
|
|
NS_LOG_ERROR("Obtained RX packets " << rxPackets << " is not expected!");
|
|
exit(1);
|
|
}
|
|
if (maxExpectedPackets > 0 && rxPackets > maxExpectedPackets)
|
|
{
|
|
NS_LOG_ERROR("Obtained RX packets " << rxPackets << " is not expected!");
|
|
exit(1);
|
|
}
|
|
if ((throughput * (1 + tolerance)) < minExpectedThroughput)
|
|
{
|
|
NS_LOG_ERROR("Obtained throughput " << throughput << " is not expected!");
|
|
exit(1);
|
|
}
|
|
if (maxExpectedThroughput > 0 && (throughput > (maxExpectedThroughput * (1 + tolerance))))
|
|
{
|
|
NS_LOG_ERROR("Obtained throughput " << throughput << " is not expected!");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
Simulator::Destroy();
|
|
return 0;
|
|
}
|