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

Popular posts from this blog

How to Create Ubuntu 24.04 Bootable USB Using Rufus [Step-by-Step Guide]

Installing ns3 in Ubuntu 22.04 | Complete Instructions

NS2 (NS-2.35) Installation in Ubuntu 11.10