Simulate Stop-and-Wait ARQ over a point-to-point link with varying bit-error rates (0–20%) | NS3 Project 30
Simulate Stop-and-Wait ARQ over a point-to-point link with varying bit-error rates (0–20%) and compare throughput & delay.
LLM used : Claude Sonnet 4.6
Prompt used : Give appropriate code for the following question along with steps to execute it in WSL setup. Make sure it generates suitable netanim files to showcase animation and tracemetrics and FlowMonitor results for each error rate. Also plot a gnuplot showcasing variances in data along with varying error rates.
Source Code:
file.cc
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/applications-module.h"
#include "ns3/netanim-module.h"
#include "ns3/mobility-module.h"
#include "ns3/flow-monitor-module.h"
using namespace ns3;
Ptr<PacketSink> sinkApp;
std::ofstream throughputFile;
static void
CalculateThroughput()
{
double cur = (sinkApp->GetTotalRx() * 8.0) / 1e6;
throughputFile << Simulator::Now().GetSeconds() << "\t" << cur << std::endl;
Simulator::Schedule(Seconds(1.0), &CalculateThroughput);
}
static void
CwndTracer(uint32_t oldCwnd, uint32_t newCwnd)
{
std::cout << Simulator::Now().GetSeconds() << "\t" << newCwnd << std::endl;
}
int main(int argc, char *argv[])
{
double errorRate = 0.0;
CommandLine cmd;
cmd.AddValue("errorRate", "Bit error rate (0-0.2)", errorRate);
cmd.Parse(argc, argv);
// ── Stop-and-Wait: cwnd=1, no delayed ACK ──────────────────────────────
Config::SetDefault("ns3::TcpSocket::InitialCwnd", UintegerValue(1));
Config::SetDefault("ns3::TcpSocket::DelAckCount", UintegerValue(0));
NodeContainer nodes;
nodes.Create(2);
MobilityHelper mobility;
mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
mobility.Install(nodes);
PointToPointHelper p2p;
p2p.SetDeviceAttribute("DataRate", StringValue("1Mbps"));
p2p.SetChannelAttribute("Delay", StringValue("10ms"));
NetDeviceContainer devices = p2p.Install(nodes);
// ── Bit-error model ───────────────────────────────────────────────────────
if (errorRate > 0.0)
{
Ptr<RateErrorModel> em = CreateObject<RateErrorModel>();
em->SetAttribute("ErrorRate", DoubleValue(errorRate));
em->SetAttribute("ErrorUnit", StringValue("ERROR_UNIT_BIT"));
// Schedule AFTER handshake — source starts at 1.0s, RTT=20ms
// so connection is established by ~1.1s, we attach at 1.5s to be safe
Simulator::Schedule(Seconds(1.5), [em, devices]() mutable {
devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(em));
std::cout << "[INFO] Error model attached at t=1.5s" << std::endl;
});
}
InternetStackHelper stack;
stack.Install(nodes);
Ipv4AddressHelper address;
address.SetBase("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer interfaces = address.Assign(devices);
uint16_t port = 8080;
// ── Sink ───────────────────────────────────────────────────────────────
PacketSinkHelper sink("ns3::TcpSocketFactory",
InetSocketAddress(Ipv4Address::GetAny(), port));
ApplicationContainer sinkContainer = sink.Install(nodes.Get(1));
sinkContainer.Start(Seconds(0.0));
sinkContainer.Stop(Seconds(20.0));
sinkApp = StaticCast<PacketSink>(sinkContainer.Get(0));
// ── Source ─────────────────────────────────────────────────────────────
BulkSendHelper source("ns3::TcpSocketFactory",
InetSocketAddress(interfaces.GetAddress(1), port));
source.SetAttribute("MaxBytes", UintegerValue(0));
ApplicationContainer sourceApp = source.Install(nodes.Get(0));
sourceApp.Start(Seconds(1.0));
sourceApp.Stop(Seconds(20.0));
// ── CwndTracer ─────────────────────────────────────────────────────────
Simulator::Schedule(Seconds(1.1), []() {
Config::ConnectWithoutContext(
"/NodeList/0/$ns3::TcpL4Protocol/SocketList/*/CongestionWindow",
MakeCallback(&CwndTracer));
});
// ── ASCII trace ────────────────────────────────────────────────────────
AsciiTraceHelper ascii;
std::ostringstream traceName;
traceName << "stopwait_" << errorRate << ".tr";
p2p.EnableAsciiAll(ascii.CreateFileStream(traceName.str()));
// ── Throughput log ─────────────────────────────────────────────────────
std::ostringstream tpFileName;
tpFileName << "throughput_" << errorRate << ".dat";
throughputFile.open(tpFileName.str());
Simulator::Schedule(Seconds(1.0), &CalculateThroughput);
// ── FlowMonitor ────────────────────────────────────────────────────────
FlowMonitorHelper flowHelper;
Ptr<FlowMonitor> monitor = flowHelper.InstallAll();
// ── NetAnim ────────────────────────────────────────────────────────────
std::ostringstream animName;
animName << "stopwait_" << errorRate << ".xml";
AnimationInterface anim(animName.str());
anim.SetConstantPosition(nodes.Get(0), 10.0, 20.0);
anim.SetConstantPosition(nodes.Get(1), 60.0, 20.0);
anim.UpdateNodeDescription(nodes.Get(0), "Sender");
anim.UpdateNodeDescription(nodes.Get(1), "Receiver");
Simulator::Stop(Seconds(20.0));
Simulator::Run();
// ── FlowMonitor results ────────────────────────────────────────────────
monitor->CheckForLostPackets();
Ptr<Ipv4FlowClassifier> classifier =
DynamicCast<Ipv4FlowClassifier>(flowHelper.GetClassifier());
std::map<FlowId, FlowMonitor::FlowStats> stats = monitor->GetFlowStats();
// Save FlowMonitor XML
std::ostringstream fmXml;
fmXml << "flowmon_" << errorRate << ".xml";
monitor->SerializeToXmlFile(fmXml.str(), true, true);
// Print to terminal + append to results file
std::ofstream resultsFile("results.dat", std::ios::app);
std::cout << "\n========== FlowMonitor Results (BER=" << errorRate*100 << "%) ==========" << std::endl;
std::cout << std::left
<< std::setw(8) << "FlowID"
<< std::setw(20) << "Throughput(Kbps)"
<< std::setw(20) << "AvgDelay(ms)"
<< std::setw(20) << "LostPackets"
<< std::setw(20) << "PDR(%)"
<< std::endl;
for (auto &entry : stats)
{
Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow(entry.first);
if (t.destinationPort != port) continue;
FlowMonitor::FlowStats fs = entry.second;
double duration = fs.timeLastRxPacket.GetSeconds()
- fs.timeFirstTxPacket.GetSeconds();
double throughput = (duration > 0)
? (fs.rxBytes * 8.0) / duration / 1000.0
: 0.0;
double avgDelay = (fs.rxPackets > 0)
? fs.delaySum.GetSeconds() * 1000.0 / fs.rxPackets
: 0.0;
double pdr = (fs.txPackets > 0)
? (double)fs.rxPackets / fs.txPackets * 100.0
: 0.0;
std::cout << std::left
<< std::setw(8) << entry.first
<< std::setw(20) << throughput
<< std::setw(20) << avgDelay
<< std::setw(20) << fs.lostPackets
<< std::setw(20) << pdr
<< std::endl;
// Append one line per run: BER Throughput Delay PDR
std::ofstream resultsFile("results.dat", std::ios::app);
// Write header only on first run (when file is empty)
if (resultsFile.tellp() == 0)
{
resultsFile << "BER%\tThroughput(Kbps)\tAvgDelay(ms)\tPDR%\n";
}
resultsFile << errorRate * 100 << "\t"
<< throughput << "\t"
<< avgDelay << "\t"
<< pdr << "\n";
}
resultsFile.close();
throughputFile.close();
Simulator::Destroy();
return 0;
}
Output:
For 0% error rate
./ns3 run “scratch/file.cc –errorRate=0.00”
FlowMonitor Results:
Tracemetrics Analysis:
Netanimation:
BER = 0% (t = 2.265s)
Two packets visible simultaneously — one going Sender → Receiver and one Receiver → Sender (ACK). This is the Stop-and-Wait handshake working perfectly. High packet density confirms clean transmission with no errors.
For 5% error rate:
./ns3 run “scratch/24BPS1069.cc –errorRate=0.05”
FlowMonitor Results:
Tracemtrics Analysis:
Netanimation:
BER = 5% (t = 1.0025s)
Single packet just leaving the Sender, still in the first half of the link. Simulation just started — TCP handshake SYN packet in transit. Fewer simultaneous packets visible compared to BER=0 due to error-induced retransmissions slowing down the flow.
For 10% error rate:
./ns3 run “scratch/24BPS1069.cc –errorRate=0.10”
FlowMonitor Results:
Tracemetrics Analysis:
Netanimation:
BER = 10% (t = 4.0075s)
Single packet in transit heading toward Receiver. Noticeably fewer packets on the link compared to BER=0 at the same time — errors are causing retransmit delays and the sender is waiting longer before sending the next packet.
For 15% error rate:
./ns3 run “scratch/24BPS1069.cc –errorRate=0.15”
FlowMonitor Results:
Tracemetrics Analysis:
Netanimation:
BER = 15% (t = 4.005s)
Packet appears stalled near the midpoint of the link. At this BER level, frequent retransmissions cause the sender to pause often — the animation visually shows slower packet movement and longer idle gaps between transmissions.
For 20% error rate:
./ns3 run “scratch/24BPS1069.cc –errorRate=0.20”
FlowMonitor Results:
Tracemetrics Analysis:
Netanimation:
BER = 20% (t = 1.0075s)
Only one packet visible very close to the Sender, barely past the starting point. At 20% BER the link is heavily disrupted — most packets are dropped and retransmitted, so almost no forward progress is visible even at t=1s into the simulation.
GNUPLOTS:
gnuplot ~/plot_results.plt
Avg Delay vs BER
At BER=0%: ~355ms delay (high because Stop-and-Wait waits for each ACK)
At BER=5–20%: drops to ~97ms and stays flat
Same data problem — all non-zero BER runs showing identical values
PDR vs BER
At BER=0%: ~97.3% delivery ratio
At BER=5–20%: drops to ~66% and stays flat
Again reflects the repeated values issue
Throughput Over Time
BER=0% (blue) grows steadily from 0 to ~15 Mbps over 19 seconds — clean transmission, no errors
All other BER values (5%, 10%, 15%, 20%) are flat near 0 — barely any data gets through
The gap between BER=0 and the rest confirms the error model is working
Throughput vs BER
At BER=0%: ~959 Kbps (near full 1Mbps link capacity)
At BER=5% and above: drops sharply to ~851 Kbps and stays flat
The flat line from 5–20% indicates the simulation values are identical for all error runs — this is the same data issue from results.dat where 5/10/15/20 all had the same values
Comments
Post a Comment