diff --git a/CMakeLists.txt b/CMakeLists.txt index 65ab9bd..7b30ef5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,9 @@ build_lib( model/aqua-sim-mac-goal.cc model/aqua-sim-mac-sfama.cc model/aqua-sim-mac-libra.cc + model/aqua-sim-mac-trumac.cc + model/aqua-sim-mac-jmac.cc + model/aqua-sim-mac-tdma.cc model/aqua-sim-mac-uwan.cc model/aqua-sim-rmac.cc model/aqua-sim-rmac-buffer.cc @@ -52,6 +55,7 @@ build_lib( model/aqua-sim-routing-ddos.cc model/aqua-sim-attack-model.cc model/aqua-sim-trace-reader.cc + model/aqua-sim-time-tag.cc model/ndn/named-data.cc model/ndn/named-data-header.cc model/ndn/name-discovery.cc @@ -103,6 +107,9 @@ build_lib( model/aqua-sim-mac-goal.h model/aqua-sim-mac-sfama.h model/aqua-sim-mac-libra.h + model/aqua-sim-mac-trumac.h + model/aqua-sim-mac-jmac.h + model/aqua-sim-mac-tdma.h model/aqua-sim-mac-uwan.h model/aqua-sim-rmac.h model/aqua-sim-rmac-buffer.h @@ -123,6 +130,7 @@ build_lib( model/aqua-sim-routing-ddos.h model/aqua-sim-attack-model.h model/aqua-sim-trace-reader.h + model/aqua-sim-time-tag.h model/ndn/named-data.h model/ndn/named-data-header.h model/ndn/name-discovery.h @@ -258,3 +266,23 @@ build_lib_example( ${libapplications} ${libaqua-sim-ng} ) + +build_lib_example( + NAME TrumacTest + SOURCE_FILES examples/trumac_test.cc + LIBRARIES_TO_LINK ${libnetwork} + ${libenergy} + ${libmobility} + ${libapplications} + ${libaqua-sim-ng} +) + +build_lib_example( + NAME JmacTest + SOURCE_FILES examples/jmac_test.cc + LIBRARIES_TO_LINK ${libnetwork} + ${libenergy} + ${libmobility} + ${libapplications} + ${libaqua-sim-ng} +) diff --git a/README.md b/README.md index 9a331b1..83a6f27 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ After that, you should be able to run example scripts located under `src/aqua-si -------------------------------------- ## Running Examples -### LIBRA MAC protocol example +### LIBRA MAC protocol LIBRA MAC protocol is a MAC protocol for UWSNs with multi-hop transmission range control capabilities, powered by Reinforcement Learning for adaptive route selection. More description can be found in the paper [1] (See `References` section down below). @@ -104,7 +104,6 @@ After executing the script, the parsed `.txt` file should be generated that will ``` Density NodesNumber Lambda TxPackets RxPackets TxCount RxCount CollisionCount TotalEnergyConsumption EnergyPerBit TotalThroughput PDR AvgHopCount 1.28 128 0.01 12871 8936 22730 152065 174667 69036.9 0.0012 5709733134989.97 0.69 1.89 -d ``` ### ALOHA and SFAMA simulations @@ -125,6 +124,111 @@ and To process raw trace-files after simulations, similar Python-scripts are available: `print_results_aloha.py` and `print_results_sfama.py`. +### TRUMAC protocol + +TRUMAC is a token-ring based MAC protocol for UWNs that leverages spatial-temporary diversity of underwater channel to improve channel utilization under high traffic loads. Please refer to [TBD] for more details. + +To repeat simulations from [TBD], `trumac_test.cc` simulation script is available. The script compares `TRUMAC`, `TDMA`, and `ALOHA` against various network sizes and traffic rates. Here is an example: + +``` +./ns3 run "TrumacTest --seed=0 --psize=100 --nodes=40 --simStop=1000 --mac=trumac --rate=120 --dist=120 --algo=0" +``` + +where: + +`--seed`: seed for pseudo-random generator function + +`--psize:`: packet size, bytes + +`--nodes:`: number of nodes in network + +`--simStop`: total simulation time, seconds + +`--mac`: select between `trumac`, `tdma`, or `aloha` protocols + +`--rate`: rate of CBR traffic, bps + +`--dist:`: average distance between any pair of nodes + +`--algo`: next-sender-selection algorithm for TRUMAC: `0` - random selection; `1` - nearest-neighbor TSP algorithm + +After running the script, a simulation-summary `results_trumac.txt` file should appear. The summary contains 7 columns, with the following metrics: + +`1 - total number of packets originated` + +`2 - total number of packets received by the app` + +`3 - total number of packets transmitted to PHY` + +`4 - total number of packets received from PHY` + +`5 - total number of collisions at PHY` + +`6 - average MAC send-queue size` + +`7 - average E2E packet delay, ms` + +To run multiple simulation instances, and aggregate the results, please refer to `scripts/runTRUMAC.py` script. + +### JMAC protocol + +Jamming MAC (JMAC) protocol is a security-focused MAC protocol for UWSNs that aims to provide communication security in a shared unencrypted underwater channel. JMAC computes a pair-sequence of transmission schedules that are recoverable in desirable area, and not recoverable in the adversary area. Please refer to [TBD] for more details. + +To repeat simulations from [TBD], `jmac_test.cc` simulation script is available. The script compares `JMAC` and `ALOHA` protocols in a specific simulation setting with `N` sensor nodes and `M` gateways, staticly allocated at the bottom and the surface, correspondingly. The script compares performance of `JMAC` and `ALOHA` in terms of PDR, throughput, and communication security (vulnerable area) provided. Here is an example: + +``` +./ns3 run "JmacTest --seed=1 --psize=400 --rate=120 --nodes=4 --mac=jmac --sinks=1 --simStop=1000 --center_x=1000 --center_z=1000 --radius=300 --depth=400 --epochTime=10" +``` + +where: + +`--seed`: seed for pseudo-random generator function + +`--psize:`: packet size, bytes + +`--rate`: rate of CBR traffic, bps + +`--nodes:`: number of nodes in network + +`--mac`: select between `jmac`, or `aloha` protocols + +`--sinks:`: number of gateways / sinks + +`--simStop`: total simulation time, seconds + +`--center_x:`: center of a circular bottom: x-coordinate, meters + +`--center_z:`: center of a circular bottom: z-coordinate, meters + +`--radius:`: radius of random nodes at bottom, meters + +`--depth:`: depth of the bottom, meters + +`--epochTime:`: Time in-between 2 CC-requests at a node, seconds + +After running the script, a simulation-summary `jmac_results.txt` file should appear. The summary contains 9 columns, with the following metrics: + +`1 - total size of vulnerable area` + +`2 - total energy consumed, joules` + +`3 - total number of packets originated` + +`4 - total number of packets received by the app` + +`5 - total number of packets scheduled for transmission at MAC (JMAC only)` + +`6 - total number of packets sent to PHY` + +`7 - total number of packets received from PHY` + +`8 - average MAC send-queue size` + +`9 - average E2E packet delay, ms` + +To run multiple simulation instances, and aggregate the results, please refer to `scripts/runJMAC.py` script. + +**Note:** Current JMAC implementation (see line `553` in `aqua-sim-mac-jmac.cc`) does not have the `auction` algorithm presented in the paper [TBD]. For the instructions on how to implement the `auction` algorithm, please refer to [TBD], and to the MATLAB code [TBD]. -------------------------------------- ## Legacy Documentation diff --git a/examples/jmac_test.cc b/examples/jmac_test.cc new file mode 100644 index 0000000..57ffae5 --- /dev/null +++ b/examples/jmac_test.cc @@ -0,0 +1,327 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Jamming MAC (JMAC) tests + * + * Author: Dmitrii Dugaev + */ + +#include "ns3/core-module.h" +#include "ns3/network-module.h" +#include "ns3/mobility-module.h" +#include "ns3/aqua-sim-ng-module.h" +#include "ns3/applications-module.h" +#include "ns3/log.h" +#include "ns3/callback.h" + +#include + +/* + * JMAC + * + */ + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE("JammingMacTest"); + +// Return random coordinates in a circle with given radius and center, at given depth (y-coord) +Vector +getBottomCoords(double center_x, double center_z, double radius, double depth, Ptr random_stream) +{ + Vector boundry = Vector(0,0,0); + double x,z; + do + { + // x = random_stream->GetValue (-radius, radius); + // z = random_stream->GetValue (-radius, radius); + x = random_stream->GetValue (0, 2*radius); + z = random_stream->GetValue (0, 2*radius); + } + while (std::sqrt (x*x + z*z) > radius); + + boundry.x = x+center_x; + boundry.z = z+center_z; + boundry.y = depth; // the nodes are located at the bottom + return boundry; +} + +// tracebacks for the stats +double vulnerableArea = 0; +double totalEnergy = 0; +uint32_t totalScheduledPkts = 0; +uint32_t totalRecvDataPkts = 0; +uint32_t totalQueueSize = 0; +uint32_t queueCount = 0; +uint32_t totalDelay = 0; +uint32_t delayCount = 0; +uint32_t totalOrigPkts = 0; +uint32_t totalPhyTxPkts = 0; +uint32_t totalPhyRxPkts = 0; + +void +traceVulnerableArea(double area) +{ + vulnerableArea += area; +} + +void +traceEnergy(double energy) +{ + totalEnergy += energy; +} + +void +traceScheduledPkts(uint32_t scheduledPkts) +{ + totalScheduledPkts += scheduledPkts; +} + +void +traceQueueSize(uint32_t qSize) +{ + totalQueueSize += qSize; + queueCount += 1; +} + +void +traceE2EDelay(uint32_t delay_ms) +{ + totalDelay += delay_ms; + delayCount += 1; +} + +void +traceRecvDataPkts() +{ + totalRecvDataPkts += 1; +} + +void +traceRoutingRx(Ptr packet) +{ + totalRecvDataPkts += 1; +} + +void +traceOrigPkts(Ptr packet) +{ + totalOrigPkts += 1; +} + +void +tracePhyTx(Ptr packet, double noise) +{ + totalPhyTxPkts += 1; +} + +void +tracePhyRx(Ptr packet, double noise) +{ + totalPhyRxPkts += 1; +} + +int +main (int argc, char *argv[]) +{ + double simStop = 100; //seconds + uint32_t seed_no; + int nodes = 2; + int sinks = 1; + double m_dataRate = 24; + uint32_t m_packetSize = 100; + // location params, meters + double radius = 100; + double center_x = 100; + double center_z = 100; + double depth = 100; + Time epochTime = Seconds(10); + //double range = 20; + std::string m_mac_protocol = "jmac"; + + LogComponentEnable ("JammingMacTest", LOG_LEVEL_INFO); + + //to change on the fly + CommandLine cmd; + cmd.AddValue ("seed", "Seed for random generation", seed_no); + cmd.AddValue ("simStop", "Length of simulation", simStop); + cmd.AddValue ("nodes", "Amount of regular underwater nodes", nodes); + cmd.AddValue ("sinks", "Amount of underwater sinks", sinks); + cmd.AddValue ("psize", "Data packet size, bytes", m_packetSize); + cmd.AddValue ("rate", "Data rate for CBR, bps", m_dataRate); + cmd.AddValue ("radius", "Radius of random nodes at bottom, meters", radius); + cmd.AddValue ("center_x", "Center of a circular bottom: x-coordinate, meters", center_x); + cmd.AddValue ("center_z", "Center of a circular bottom: z-coordinate, meters", center_z); + cmd.AddValue ("depth", "Depth of the bottom, meters", depth); + cmd.AddValue ("epochTime", "Time in-between 2 CC-requests at a node", epochTime); + cmd.AddValue ("mac", "Select between TR-MAC and TDMA", m_mac_protocol); + cmd.Parse(argc,argv); + + std::cout << "-----------Initializing simulation-----------\n"; + + // Initialize pseudo-random generator + SeedManager::SetSeed (12345); + SeedManager::SetRun (seed_no); + Ptr random_stream = CreateObject (); + + NodeContainer nodesCon; + NodeContainer sinksCon; + nodesCon.Create(nodes); + sinksCon.Create(sinks); + + PacketSocketHelper socketHelper; + socketHelper.Install(nodesCon); + socketHelper.Install(sinksCon); + + //establish layers using helper's pre-build settings + AquaSimChannelHelper channel = AquaSimChannelHelper::Default(); + //channel.SetPropagation("ns3::AquaSimRangePropagation"); + AquaSimHelper asHelper = AquaSimHelper::Default(); + asHelper.SetChannel(channel.Create()); + + if (m_mac_protocol == "jmac") + { + asHelper.SetMac("ns3::AquaSimJammingMac", "PacketSize", IntegerValue(m_packetSize), "EpochTime", TimeValue(epochTime)); } + else if (m_mac_protocol == "aloha") + { + asHelper.SetMac("ns3::AquaSimAloha", "AckOn", IntegerValue(0)); + } + else + { + NS_FATAL_ERROR ("Unkown MAC protocol name provided!"); + } + + + asHelper.SetRouting("ns3::AquaSimRoutingDummy"); + + /* + * Set up mobility model for nodes and sinks + */ + MobilityHelper mobility; + NetDeviceContainer devices; + Ptr position = CreateObject (); + Vector boundry = Vector(0,0,0); + + std::cout << "Creating Nodes\n"; + + // Place nodes at the "bottom" - a random circle in (x,z)-plane; y - depth of the "bottom" + for (NodeContainer::Iterator i = nodesCon.Begin(); i != nodesCon.End(); i++) + { + position->Add(getBottomCoords(center_x, center_z, radius, depth, random_stream)); + Ptr newDevice = CreateObject(); + devices.Add(asHelper.Create(*i, newDevice)); + //newDevice->GetPhy()->SetTransRange(range); + } + + // Place all sinks at the center of the circle, at 0-meter depth (y=0) + // TODO: place multiple sinks at different positions + for (NodeContainer::Iterator i = sinksCon.Begin(); i != sinksCon.End(); i++) + { + boundry.x = center_x; + boundry.z = center_z; + boundry.y = 0; // sink is located at the surface + position->Add(boundry); + + Ptr newDevice = CreateObject(); + devices.Add(asHelper.Create(*i, newDevice)); + //newDevice->GetPhy()->SetTransRange(range); + } + + mobility.SetPositionAllocator(position); + mobility.Install(nodesCon); + mobility.Install(sinksCon); + + // Debug node positions + for (uint32_t i = 0; i < nodesCon.GetN(); i++) + { + Ptr mob = nodesCon.Get(i)->GetObject(); + NS_LOG_DEBUG("Node " << i << " (x,y,z)-position: (" << mob->GetPosition().x << + ",\t" << mob->GetPosition().y << ",\t" << mob->GetPosition().z << ")"); + } + for (uint32_t i = 0; i < sinksCon.GetN(); i++) + { + Ptr mob = sinksCon.Get(i)->GetObject(); + NS_LOG_DEBUG("Sink " << i << " (x,y,z)-position: (" << mob->GetPosition().x << + ",\t" << mob->GetPosition().y << ",\t" << mob->GetPosition().z << ")"); + } + + // Application and sockets + PacketSocketAddress socket; + socket.SetAllDevices(); + socket.SetPhysicalAddress (devices.Get(nodes)->GetAddress()); //Set dest to first sink (nodes+1 device) + socket.SetProtocol (0); + + OnOffHelper app ("ns3::PacketSocketFactory", Address (socket)); + + char duration_on[300]; + char duration_off[300]; + + sprintf(duration_on, "ns3::ExponentialRandomVariable[Mean=%f]", (m_packetSize * 8) / m_dataRate); + sprintf(duration_off, "ns3::ExponentialRandomVariable[Mean=%f]", 1 / 100.0); // lambda + + app.SetAttribute ("OnTime", StringValue (duration_on)); + app.SetAttribute ("OffTime", StringValue (duration_off)); + + app.SetAttribute ("DataRate", DataRateValue (m_dataRate)); + app.SetAttribute ("PacketSize", UintegerValue (m_packetSize)); + + ApplicationContainer apps = app.Install (nodesCon); + apps.Start (Seconds (0.5)); + apps.Stop (Seconds (simStop + 1)); + + Ptr sinkNode = sinksCon.Get(0); + TypeId psfid = TypeId::LookupByName ("ns3::PacketSocketFactory"); + + Ptr sinkSocket = Socket::CreateSocket (sinkNode, psfid); + sinkSocket->Bind (socket); + + if (m_mac_protocol == "jmac") { + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Mac/AreaTrace", MakeCallback (&traceVulnerableArea)); + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Mac/EnergyTrace", MakeCallback (&traceEnergy)); + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Mac/ScheduledPktsTrace", MakeCallback (&traceScheduledPkts)); + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Mac/RecvDataPktsTrace", MakeCallback (&traceRecvDataPkts)); + } + if (m_mac_protocol == "aloha") { + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Mac/RoutingRx", MakeCallback (&traceRoutingRx)); + } + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Mac/QueueSizeTrace", MakeCallback (&traceQueueSize)); + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Mac/E2EDelayTrace", MakeCallback (&traceE2EDelay)); + Config::ConnectWithoutContext ("/NodeList/*/ApplicationList/*/$ns3::Application/Tx", MakeCallback (&traceOrigPkts)); + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Phy/Tx", MakeCallback (&tracePhyTx)); + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Phy/Rx", MakeCallback (&tracePhyRx)); + +/* + * For channel trace driven simulation + */ +/* + AquaSimTraceReader tReader; + tReader.SetChannel(asHelper.GetChannel()); + if (tReader.ReadFile("channelTrace.txt")) NS_LOG_DEBUG("Trace Reader Success"); + else NS_LOG_DEBUG("Trace Reader Failure"); +*/ + + Packet::EnablePrinting (); //for debugging purposes + std::cout << "-----------Running Simulation-----------\n"; + Simulator::Stop(Seconds(simStop)); + Simulator::Run(); + asHelper.GetChannel()->PrintCounters(); + Simulator::Destroy(); + + // print the stats + // std::cout << "VULNERABLE AREA: " << vulnerableArea << "\n"; + // std::cout << "TOTAL ENERGY: " << totalEnergy << "\n"; + + std::ofstream results ("jmac_results.txt", std::ofstream::app); + results << vulnerableArea << "\t" << totalEnergy << "\t" + << totalOrigPkts << "\t" + << totalRecvDataPkts << "\t" + << totalScheduledPkts << "\t" + << totalPhyTxPkts << "\t" + << totalPhyRxPkts << "\t" + << std::setprecision (4) << (double) totalQueueSize/queueCount << "\t" + << std::setprecision (8) << (double) totalDelay/delayCount << "\t" + << "\n"; + // + + std::cout << "fin.\n"; + return 0; +} diff --git a/examples/trumac_test.cc b/examples/trumac_test.cc new file mode 100644 index 0000000..5d99086 --- /dev/null +++ b/examples/trumac_test.cc @@ -0,0 +1,289 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * MAC tests across TRUMAC, ALOHA and TDMA protocols + * + * Author: Dmitrii Dugaev + */ + +#include "ns3/core-module.h" +#include "ns3/network-module.h" +#include "ns3/mobility-module.h" +#include "ns3/aqua-sim-ng-module.h" +#include "ns3/applications-module.h" +#include "ns3/log.h" +#include "ns3/callback.h" + +#include + +/* + * TRUMAC + * + */ + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE("TrumacTest"); + + +// tracebacks for the stats +uint32_t totalRecvDataPkts = 0; +uint32_t totalQueueSize = 0; +uint32_t queueCount = 0; +uint32_t totalDelay = 0; +uint32_t delayCount = 0; +uint32_t totalOrigPkts = 0; +uint32_t totalPhyTxPkts = 0; +uint32_t totalPhyRxPkts = 0; +uint32_t totalPhyRxColls = 0; + + +void +traceQueueSize(uint32_t qSize) +{ + totalQueueSize += qSize; + queueCount += 1; +} + +void +traceE2EDelay(uint32_t delay_ms) +{ + totalDelay += delay_ms; + delayCount += 1; +} + +void +traceRecvDataPkts(Ptr) +{ + totalRecvDataPkts += 1; +} + +void +traceOrigPkts(Ptr packet) +{ + totalOrigPkts += 1; +} + +void +tracePhyTx(Ptr packet, double noise) +{ + totalPhyTxPkts += 1; +} + +void +tracePhyRx(Ptr packet, double noise) +{ + totalPhyRxPkts += 1; +} + +void +tracePhyRxColl() +{ + totalPhyRxColls += 1; +} + +int +main (int argc, char *argv[]) +{ + double simStop = 100; //seconds + uint32_t seed_no; + int nodes = 2; + double dist = 100; + double m_dataRate = 24; + uint32_t m_packetSize = 40; + uint32_t m_algo_id = 0; + + std::string m_mac_protocol = "trumac"; + + LogComponentEnable ("TrumacTest", LOG_LEVEL_INFO); + + //to change on the fly + CommandLine cmd; + cmd.AddValue ("seed", "Seed for random generation", seed_no); + cmd.AddValue ("simStop", "Length of simulation", simStop); + cmd.AddValue ("nodes", "Amount of regular underwater nodes", nodes); + cmd.AddValue ("psize", "Data packet size, bytes", m_packetSize); + cmd.AddValue ("rate", "Data rate for CBR, bps", m_dataRate); + cmd.AddValue ("mac", "Select between TR-MAC and TDMA", m_mac_protocol); + cmd.AddValue ("dist", "Distance between two nodes, in meters", dist); + cmd.AddValue ("algo", "Switch between random selection and sub-optimal TSP-cucle", m_algo_id); + cmd.Parse(argc,argv); + + std::cout << "-----------Initializing simulation-----------\n"; + + // Initialize pseudo-random generator + SeedManager::SetSeed (12345); + SeedManager::SetRun (seed_no); + Ptr random_stream = CreateObject (); + + NodeContainer nodesCon; + nodesCon.Create(nodes); + + PacketSocketHelper socketHelper; + socketHelper.Install(nodesCon); + + //establish layers using helper's pre-build settings + AquaSimChannelHelper channel = AquaSimChannelHelper::Default(); + channel.SetPropagation("ns3::AquaSimRangePropagation"); + AquaSimHelper asHelper = AquaSimHelper::Default(); + asHelper.SetChannel(channel.Create()); + + if (m_mac_protocol == "trumac") + { + asHelper.SetMac("ns3::AquaSimTrumac", "PacketSize", IntegerValue(m_packetSize), "StartNodeId", UintegerValue(0), + "TotalNodes", UintegerValue(nodes), "AlgoId", UintegerValue(m_algo_id), + "GuardTime", TimeValue(MilliSeconds(1))); + } + else if (m_mac_protocol == "tdma") + { + asHelper.SetMac("ns3::AquaSimTdmaMac", "TdmaSlotPeriod", UintegerValue(nodes), "TdmaSlotDuration", TimeValue(Seconds(0.6))); + } + else if (m_mac_protocol == "aloha") + { + asHelper.SetMac("ns3::AquaSimAloha", "AckOn", IntegerValue(0)); + } + else + { + NS_FATAL_ERROR ("Unkown MAC protocol name provided!"); + } + + asHelper.SetRouting("ns3::AquaSimRoutingDummy"); + + /* + * Set up mobility model for nodes and sinks + */ + MobilityHelper mobility; + NetDeviceContainer devices; + + std::cout << "Creating Nodes\n"; + for (NodeContainer::Iterator i = nodesCon.Begin(); i != nodesCon.End(); i++) + { + Ptr newDevice = CreateObject(); + devices.Add(asHelper.Create(*i, newDevice)); + newDevice->GetPhy()->SetTransRange(1500); + if (m_mac_protocol == "tdma") + { + newDevice->GetMac()->SetAttribute("TdmaSlotNumber", UintegerValue(newDevice->GetNode()->GetId())); + } + } + + // Allocate nodes randomly within a circle + // mobility.SetPositionAllocator("ns3::UniformDiscPositionAllocator", "X", DoubleValue(0), + // "Y", DoubleValue(0), "rho", DoubleValue(dist/2)); // TODO: change to Max.Tx.Range/2 + // mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel"); + ObjectFactory pos; + pos.SetTypeId("ns3::UniformDiscPositionAllocator"); + pos.Set("X", DoubleValue(0)); + pos.Set("Y", DoubleValue(0)); + pos.Set("rho", DoubleValue(dist/2)); + Ptr positionAlloc = pos.Create ()->GetObject (); + mobility.SetPositionAllocator(positionAlloc); + mobility.SetMobilityModel ("ns3::RandomWaypointMobilityModel", + "Speed", StringValue ("ns3::ConstantRandomVariable[Constant=0.5]"), + "Pause", StringValue ("ns3::UniformRandomVariable[Min=0.1|Max=3.0]"), + "PositionAllocator", PointerValue (positionAlloc)); + + mobility.Install(nodesCon); + + // // Allocate nodes in a fixed grid + // mobility.SetPositionAllocator ("ns3::GridPositionAllocator", + // "MinX", DoubleValue (0.0), + // "MinY", DoubleValue (0.0), + // "DeltaX", DoubleValue (dist), + // "DeltaY", DoubleValue (dist), + // "GridWidth", UintegerValue (sqrt(nodes)), + // // "GridWidth", UintegerValue (2), // for 3-node triangle test + // "LayoutType", StringValue ("RowFirst")); + // mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel"); + // mobility.Install(nodesCon); + + // Debug node positions + for (uint32_t i = 0; i < nodesCon.GetN(); i++) + { + Ptr mob = nodesCon.Get(i)->GetObject(); + NS_LOG_DEBUG("Node " << i << " (x,y,z)-position: (" << mob->GetPosition().x << + ",\t" << mob->GetPosition().y << ",\t" << mob->GetPosition().z << ")"); + } + + int j = 0; + char duration_on[300]; + char duration_off[300]; + for (NodeContainer::Iterator i = nodesCon.Begin(); i != nodesCon.End(); i++) + { + AquaSimApplicationHelper app ("ns3::PacketSocketFactory", nodes); + + // app.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1]")); + // app.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0]")); + // app.SetAttribute ("DataRate", DataRateValue (m_dataRate)); + // app.SetAttribute ("PacketSize", UintegerValue (m_packetSize)); + + sprintf(duration_on, "ns3::ExponentialRandomVariable[Mean=%f]", (m_packetSize * 8) / m_dataRate); + sprintf(duration_off, "ns3::ExponentialRandomVariable[Mean=%f]", 1 / 100.0); // lambda + + app.SetAttribute ("OnTime", StringValue (duration_on)); + app.SetAttribute ("OffTime", StringValue (duration_off)); + + app.SetAttribute ("DataRate", DataRateValue (m_dataRate)); + app.SetAttribute ("PacketSize", UintegerValue (m_packetSize)); + + ApplicationContainer apps = app.Install (nodesCon.Get(j)); + + // // start traffic only on a single node + // if (j == 0) + // { + apps.Start (Seconds (0.5)); + apps.Stop (Seconds (simStop + 1)); + // } + // else + // { + // apps.Start (Seconds (100000)); + // apps.Stop (Seconds (100000)); + // } + + // // generate 1000 packets side-by-side + // apps.Start (Seconds (0.001)); + // apps.Stop (Seconds (0.08 * 1001 + 0.001)); + + j++; + } + + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Mac/RoutingRx", MakeCallback (&traceRecvDataPkts)); + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Mac/QueueSizeTrace", MakeCallback (&traceQueueSize)); + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Mac/E2EDelayTrace", MakeCallback (&traceE2EDelay)); + Config::ConnectWithoutContext ("/NodeList/*/ApplicationList/*/$ns3::Application/Tx", MakeCallback (&traceOrigPkts)); + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Phy/Tx", MakeCallback (&tracePhyTx)); + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Phy/Rx", MakeCallback (&tracePhyRx)); + Config::ConnectWithoutContext ("/NodeList/*/DeviceList/*/$ns3::NetDevice/Phy/RxColl", MakeCallback (&tracePhyRxColl)); + +/* + * For channel trace driven simulation + */ +/* + AquaSimTraceReader tReader; + tReader.SetChannel(asHelper.GetChannel()); + if (tReader.ReadFile("channelTrace.txt")) NS_LOG_DEBUG("Trace Reader Success"); + else NS_LOG_DEBUG("Trace Reader Failure"); +*/ + + Packet::EnablePrinting (); //for debugging purposes + std::cout << "-----------Running Simulation-----------\n"; + // Simulator::Stop(Seconds(simStop + 0.5)); + Simulator::Stop(Seconds(simStop)); + Simulator::Run(); + asHelper.GetChannel()->PrintCounters(); + Simulator::Destroy(); + + // print the stats + std::ofstream results ("results_trumac.txt", std::ofstream::app); + results << totalOrigPkts << "\t" + << totalRecvDataPkts << "\t" + << totalPhyTxPkts << "\t" + << totalPhyRxPkts << "\t" + << totalPhyRxColls << "\t" + << std::setprecision (4) << (double) totalQueueSize/queueCount << "\t" + << std::setprecision (8) << (double) totalDelay/delayCount << "\t" + << "\n"; + // + + std::cout << "fin.\n"; + return 0; +} diff --git a/model/aqua-sim-header-mac.cc b/model/aqua-sim-header-mac.cc index 10ca315..fb2c74d 100644 --- a/model/aqua-sim-header-mac.cc +++ b/model/aqua-sim-header-mac.cc @@ -869,6 +869,291 @@ LocalizationHeader::GetInstanceTypeId(void) const return GetTypeId(); } +/* + * JMAC header + */ +JammingMacHeader::JammingMacHeader() +{ + // // Initialize schedule map + // // TODO: make the number of nodes a variable (it's fixed to 2 now) + // for (uint8_t i=0; i<2; i++) + // { + // m_delays_ms.insert(std::make_pair(i, 65535)); // 2**16-1 is the maximum possible delay in ms - this means that the delay is NOT set, by default + // } +} + +JammingMacHeader::~JammingMacHeader() +{ +} + +TypeId +JammingMacHeader::GetTypeId() +{ + static TypeId tid = TypeId("ns3::JammingMacHeader") + .SetParent
() + .AddConstructor() + ; + return tid; +} + +uint32_t +JammingMacHeader::GetSerializedSize(void) const +{ + // DATA + if (m_ptype == 0) + { + // ptype + node_id [in bytes] + return 2; + } + // CC-request + if (m_ptype == 1) + { + return 2 + 3*4; // (x,y,z) coordinates each 4-byte long + } + // CS-reply (contains a schedule) + if (m_ptype == 2) + { + return 2 + 8 + 2*GetNodesAmount(); // 16-bits for every node (64 nodes max, currently) + } + + // This should not happen + NS_FATAL_ERROR ("JammingMAC packet-type is invalid!"); + return 0; +} + +void +JammingMacHeader::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + i.WriteU8 ((uint8_t)(m_ptype)); + i.WriteU8 ((uint8_t)(m_node_id)); + if (m_ptype == 2) + { + // Serialize node_list + i.WriteU64(m_node_list); + // Serialize the schedule + for (uint8_t j=0; j<64; j++) + { + if (GetNodeBit(j) == 1) + { + i.WriteU16(m_delays_ms.at(j)); + } + } + } + if (m_ptype == 1) + { + // serialize coordinates + i.WriteU32(m_x_coord); + i.WriteU32(m_y_coord); + i.WriteU32(m_z_coord); + } +} + +uint32_t +JammingMacHeader::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + m_ptype = i.ReadU8(); + m_node_id = i.ReadU8(); + if (m_ptype == 2) + { + // Deserialize node_list + m_node_list = i.ReadU64(); + // Deserialize the schedule + for (uint8_t j=0; j<64; j++) + { + if (GetNodeBit(j) == 1) + { + m_delays_ms.insert(std::make_pair(j, i.ReadU16())); + } + } + } + if (m_ptype == 1) + { + m_x_coord = i.ReadU32(); + m_y_coord = i.ReadU32(); + m_z_coord = i.ReadU32(); + } + return GetSerializedSize(); +} + +void +JammingMacHeader::Print (std::ostream &os) const +{ + os << "JammingMAC Header: "; + + os << "PType=" << int(m_ptype) << " "; + os << "\n"; +} + +TypeId +JammingMacHeader::GetInstanceTypeId(void) const +{ + return GetTypeId(); +} + +void +JammingMacHeader::SetPType(uint8_t ptype) +{ + m_ptype = ptype; +} + +uint8_t +JammingMacHeader::GetPType() +{ + return m_ptype; +} + +void +JammingMacHeader::SetSchedule(uint8_t node_id, uint16_t delay_ms) +{ + m_delays_ms.insert(std::make_pair(node_id, delay_ms)); + SetNodeBit(node_id); +} + +uint16_t +JammingMacHeader::GetSchedule(uint8_t node_id) +{ + return m_delays_ms.at(node_id); +} + +void +JammingMacHeader::SetCoordinates(Vector coords) +{ + m_x_coord = coords.x * 1000000; + m_y_coord = coords.y * 1000000; + m_z_coord = coords.z * 1000000; +} + +Vector +JammingMacHeader::GetCoordinates() +{ + Vector coords = Vector((double) m_x_coord/1000000, (double) m_y_coord/1000000, (double) m_z_coord/1000000); + return coords; +} + +void +JammingMacHeader::SetNodeBit(uint8_t node_id) +{ + int mask = 1 << node_id; // node_id serves as position + m_node_list = (m_node_list & ~mask) | ((1 << node_id) & mask); +} + +uint8_t +JammingMacHeader::GetNodeBit(uint8_t node_id) const +{ + return (((1 << 1) - 1) & (m_node_list >> node_id)); +} + +void +JammingMacHeader::SetNodeId(uint8_t node_id) +{ + m_node_id = node_id; +} + +uint8_t +JammingMacHeader::GetNodeId() +{ + return m_node_id; +} + +uint8_t +JammingMacHeader::GetNodesAmount() const +{ + uint8_t count = 0; + uint64_t n = m_node_list; + while (n) + { + count += n & 1; + n >>= 1; + } + return count; +} + +/* + * TRUMAC header + */ +TrumacHeader::TrumacHeader() +{ +} + +TrumacHeader::~TrumacHeader() +{ +} + +TypeId +TrumacHeader::GetTypeId() +{ + static TypeId tid = TypeId("ns3::TrumacHeader") + .SetParent
() + .AddConstructor() + ; + return tid; +} + +uint32_t +TrumacHeader::GetSerializedSize(void) const +{ + return 1+1; // ptype + next_sender_id +} + +void +TrumacHeader::Serialize (Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + i.WriteU8 ((uint8_t)(m_ptype)); + i.WriteU8 ((uint8_t)(m_next_sender_id)); +} + +uint32_t +TrumacHeader::Deserialize (Buffer::Iterator start) +{ + Buffer::Iterator i = start; + m_ptype = i.ReadU8(); + m_next_sender_id = i.ReadU8(); + return GetSerializedSize(); +} + +void +TrumacHeader::Print (std::ostream &os) const +{ + os << "TR-MAC Header: "; + + os << "PType=" << int(m_ptype) << " "; + os << "NextId=" << int(m_next_sender_id) << " "; + os << "\n"; +} + +TypeId +TrumacHeader::GetInstanceTypeId(void) const +{ + return GetTypeId(); +} + +void +TrumacHeader::SetPType(uint8_t ptype) +{ + m_ptype = ptype; +} + +uint8_t +TrumacHeader::GetPType() +{ + return m_ptype; +} + +void +TrumacHeader::SetNextNodeId(uint8_t next_node_id) +{ + m_next_sender_id = next_node_id; +} + +uint8_t +TrumacHeader::GetNextNodeId() +{ + return m_next_sender_id; +} + /* * MacLibraHeader */ diff --git a/model/aqua-sim-header-mac.h b/model/aqua-sim-header-mac.h index 0a29744..af864ad 100644 --- a/model/aqua-sim-header-mac.h +++ b/model/aqua-sim-header-mac.h @@ -350,6 +350,87 @@ class LocalizationHeader : public Header double m_confidence; }; // class LocalizationHeader +/** + * \brief JMAC header + */ +class JammingMacHeader : public Header +{ +public: + JammingMacHeader(); + virtual ~JammingMacHeader(); + static TypeId GetTypeId(void); + + void SetPType(uint8_t m_ptype); + uint8_t GetPType(); + + // Set "schedule" - delay before a packet is transmitted by given node_id + void SetSchedule(uint8_t node_id, uint16_t delay_ms); + uint16_t GetSchedule(uint8_t node_id); + + // Set/get node_id for cc-request + void SetNodeId(uint8_t node_id); + uint8_t GetNodeId(); + + // Set node-bit in the schedule + void SetNodeBit(uint8_t node_id); + uint8_t GetNodeBit(uint8_t node_id) const; + // Return an amount of nodes in the schedule (node_list) + uint8_t GetNodesAmount() const; + + // Set/get coordinates from request/reply + void SetCoordinates(Vector coords); + Vector GetCoordinates(); + + //inherited methods + virtual uint32_t GetSerializedSize(void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual void Print (std::ostream &os) const; + virtual TypeId GetInstanceTypeId(void) const; + +private: + // Packet type: 0 - DATA, 1 - CC-request, 2 - CS-reply + uint8_t m_ptype; + uint8_t m_node_id; + // Node list for CS-header + uint64_t m_node_list = 0; + // Schedule (delay) in milliseconds for given nodes + std::map m_delays_ms; + // store coordinates for cc-request + uint32_t m_x_coord; + uint32_t m_y_coord; + uint32_t m_z_coord; +}; // class JammingMacHeader + +/** + * \brief TRUMAC header + */ +class TrumacHeader : public Header +{ +public: + TrumacHeader(); + virtual ~TrumacHeader(); + static TypeId GetTypeId(void); + + void SetPType(uint8_t m_ptype); + uint8_t GetPType(); + + void SetNextNodeId(uint8_t node_id); + uint8_t GetNextNodeId(); + + //inherited methods + virtual uint32_t GetSerializedSize(void) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual void Print (std::ostream &os) const; + virtual TypeId GetInstanceTypeId(void) const; + +private: + // Packet type: 0 - DATA, 1 - Token + uint8_t m_ptype; + uint8_t m_next_sender_id; +}; // class TrumacHeader + /** * \brief mac-libra header */ diff --git a/model/aqua-sim-mac-aloha.cc b/model/aqua-sim-mac-aloha.cc index 7488e18..f23edc1 100644 --- a/model/aqua-sim-mac-aloha.cc +++ b/model/aqua-sim-mac-aloha.cc @@ -23,6 +23,7 @@ #include "aqua-sim-pt-tag.h" #include "aqua-sim-header.h" #include "aqua-sim-header-mac.h" +#include "aqua-sim-time-tag.h" #include "ns3/packet.h" #include "ns3/log.h" @@ -186,7 +187,13 @@ bool AquaSimAloha::TxProcess(Ptr pkt) pkt->AddHeader(alohaH); pkt->AddHeader(asHeader); + // Attach a timestamp tag to calculate E2E delay + AquaSimTimeTag timeTag; + timeTag.SetTime(Simulator::Now()); + pkt->AddPacketTag(timeTag); + // PktQ_.push(pkt);//push packet to the queue + m_queueSizeTrace(PktQ_.size()); //fill the next hop when sending out the packet; if(ALOHA_Status == PASSIVE && PktQ_.size() >= 1 && !m_blocked ) @@ -354,13 +361,18 @@ bool AquaSimAloha::RecvProcess(Ptr pkt) else if(alohaH.GetPType() == AlohaHeader::DATA) { //process Data packet if( recver == myAddr || recver == AquaSimAddress::GetBroadcast() ) { - pkt->RemoveHeader(asHeader); - auto cpkt = pkt->Copy(); - pkt->AddHeader(asHeader); - cpkt->RemoveHeader(alohaH); - asHeader.SetSize(asHeader.GetSize() - alohaH.GetSize()); - cpkt->AddHeader(asHeader); - SendUp(cpkt); + // trace the E2E delay + AquaSimTimeTag timeTag; + pkt->RemovePacketTag(timeTag); + m_e2eDelayTrace((Simulator::Now() - timeTag.GetTime()).GetMilliSeconds()); + // + pkt->RemoveHeader(asHeader); + auto cpkt = pkt->Copy(); + pkt->AddHeader(asHeader); + cpkt->RemoveHeader(alohaH); + asHeader.SetSize(asHeader.GetSize() - alohaH.GetSize()); + cpkt->AddHeader(asHeader); + SendUp(cpkt); if ( m_AckOn && (recver != AquaSimAddress::GetBroadcast())) ReplyACK(pkt->Copy()); diff --git a/model/aqua-sim-mac-jmac.cc b/model/aqua-sim-mac-jmac.cc new file mode 100644 index 0000000..4cab055 --- /dev/null +++ b/model/aqua-sim-mac-jmac.cc @@ -0,0 +1,818 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2020 The City University of New York + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Dmitrii Dugaev + */ + +#include "aqua-sim-mac-jmac.h" +#include "aqua-sim-header.h" +#include "aqua-sim-header-mac.h" +#include "aqua-sim-address.h" + +#include "ns3/log.h" +#include "ns3/integer.h" +#include "ns3/simulator.h" + +#include +#include +#include + +#include +#include +#define SIZE 2048 + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE("AquaSimJammingMac"); +NS_OBJECT_ENSURE_REGISTERED(AquaSimJammingMac); + + +/* ========= +Jamming MAC +============ */ + +AquaSimJammingMac::AquaSimJammingMac() +{ + m_rand = CreateObject (); + m_packetSize = 800; // bytes + m_epoch_time = Seconds(10); + m_guard_time = MilliSeconds(1); + m_cs_delay = Seconds(1); // this is a delay of a CS-reply sent from sink to sensor nodes + m_data_delay = Seconds(20); + + // schedule the very first cycle + Simulator::Schedule(Seconds(1), &AquaSimJammingMac::initCycle, this); +} + +TypeId +AquaSimJammingMac::GetTypeId() +{ + static TypeId tid = TypeId("ns3::AquaSimJammingMac") + .SetParent() + .AddConstructor() + .AddAttribute("PacketSize", "Size of packet", + IntegerValue(800), + MakeIntegerAccessor (&AquaSimJammingMac::m_packetSize), + MakeIntegerChecker ()) + .AddAttribute ("EpochTime", "Interval between cc-request generations", + TimeValue(Seconds(10)), + MakeTimeAccessor(&AquaSimJammingMac::m_epoch_time), + MakeTimeChecker()) + .AddAttribute ("GuardTime", "Interval in-between the transmissions in a train", + TimeValue(MilliSeconds(100)), + MakeTimeAccessor(&AquaSimJammingMac::m_guard_time), + MakeTimeChecker()) + .AddTraceSource ("AreaTrace", + "Trace an area calculation event", + MakeTraceSourceAccessor (&AquaSimJammingMac::m_trace_area), "ns3::Packet::TracedCallback") + .AddTraceSource ("EnergyTrace", + "Trace an energy calculation event", + MakeTraceSourceAccessor (&AquaSimJammingMac::m_trace_energy), "ns3::Packet::TracedCallback") + .AddTraceSource ("ScheduledPktsTrace", + "Trace a packet in a schedule", + MakeTraceSourceAccessor (&AquaSimJammingMac::m_scheduled_pkts), "ns3::Packet::TracedCallback") + .AddTraceSource ("RecvDataPktsTrace", + "Trace a reception event of a data packet", + MakeTraceSourceAccessor (&AquaSimJammingMac::m_recv_data_pkts), "ns3::Packet::TracedCallback") + ; + return tid; +} + +int64_t +AquaSimJammingMac::AssignStreams (int64_t stream) +{ + NS_LOG_FUNCTION (this << stream); + m_rand->SetStream(stream); + return 1; +} + +void +AquaSimJammingMac::initCycle() +{ + // Start the very first Channel Competition (CC) phase + StartCC(); + + // set the very first timer at the sink-node to broadcast the schedule + Simulator::Schedule(m_epoch_time, &AquaSimJammingMac::TriggerCsReply, this); +} + +void +AquaSimJammingMac::StartCC() +{ + if (m_sendQueue.size() != 0) + { + // Log epoch number (amount of cc --> cs --> data handshakes) + // std::cout << "EPOCH No: " << m_epoch_no << "\n"; + // Start cc phase + // Read aqua-sim header info from a data packet + AquaSimHeader ash; + m_sendQueue.front()->PeekHeader(ash); + + // Create a CC-request + Ptr cc_request = Create(); + MacHeader mach; + mach.SetSA(AquaSimAddress::ConvertFrom(m_device->GetAddress()) ); + mach.SetDA(ash.GetDAddr()); + mach.SetDemuxPType(MacHeader::UWPTYPE_OTHER); + JammingMacHeader jamh; + jamh.SetPType(1); // 1 - cc-request + jamh.SetNodeId(m_device->GetNode()->GetId()); + // set coordinates + Ptr mob = m_device->GetNode()->GetObject(); + Vector coords = Vector(mob->GetPosition().x, mob->GetPosition().y, mob->GetPosition().z); + // std::cout << "SENT COORDS: " << coords << "\n"; + jamh.SetCoordinates(coords); + + cc_request->AddHeader(jamh); + cc_request->AddHeader(mach); + cc_request->AddHeader(ash); + + // Send CC-request towards the sink, using pure-ALOHA + Simulator::Schedule(Seconds(GetBackoff()), &AquaSimJammingMac::SendPacketAloha, this, cc_request->Copy()); + m_epoch_no++; + } + + // Sleep for a CS-reception phase and then send data packets in the DATA-send phase + Simulator::Schedule(m_epoch_time + m_cs_delay, &AquaSimJammingMac::StartData, this); +} + +void +AquaSimJammingMac::StartData() +{ + // Schedule packet transmissions based on the schedule + if (m_current_schedule.GetNodeBit(m_device->GetNode()->GetId()) == 1) + { + AquaSimHeader ash; + MacHeader mach; + + Time tx_delay = MilliSeconds(m_current_schedule.GetSchedule(m_device->GetNode()->GetId())); + // Get data packet from queue + Ptr data_packet = m_sendQueue.front(); + m_sendQueue.pop_front(); + // Attach mac-headers to packet + data_packet->RemoveHeader(ash); + mach.SetSA(ash.GetSAddr()); + mach.SetDA(ash.GetDAddr()); + mach.SetDemuxPType(MacHeader::UWPTYPE_OTHER); + JammingMacHeader data_h; + data_h.SetPType(0); // 0 - data packet + data_h.SetNodeId(m_device->GetNode()->GetId()); + data_packet->AddHeader(data_h); + data_packet->AddHeader(mach); + data_packet->AddHeader(ash); + + Simulator::Schedule(tx_delay, &AquaSimJammingMac::SendDown, this, data_packet, ns3::NIDLE); + + } + // Schedule the next CC-phase + Simulator::Schedule(m_data_delay, &AquaSimJammingMac::StartCC, this); +} + +void +AquaSimJammingMac::SendPacketAloha(Ptr pkt) +{ + // // Check net_device status + // // If it is IDLE, send packet down to PHY immediately + // // otherwise, do random backoff again + // // if (m_device->GetTransmissionStatus() == TransStatus::NIDLE) + // if (m_device->GetTransmissionStatus() != TransStatus::SEND) + // { + // Call parent method to send packet down to PHY + SendDown(pkt); + // } + // else + // { + // // Do another backoff + // Simulator::Schedule(Seconds(GetBackoff()), &AquaSimJammingMac::SendPacketAloha, this, pkt->Copy()); + // } +} + +// Return a random number in range +double AquaSimJammingMac::GetBackoff() +{ + return m_rand->GetValue(0.08*0, 0.08*100); +} + +std::map +AquaSimJammingMac::getPairsFromFile(uint32_t nNodes, char matrixString[]) +{ + std::map pairs; + + uint32_t node1, node2 = 0; // nodes in a pair + for (uint32_t i=0; i<(nNodes*nNodes); i++) + { + // std::cout << matrixString[i] << "\n"; + if (matrixString[i] == '1') + { + node1 = i / nNodes; + node2 = i % nNodes; + // std::cout << node1 << ", " << node2 << "\n"; + pairs.insert(std::make_pair(node1, node2)); + } + } + + return pairs; +} + +uint16_t +AquaSimJammingMac::getDistanceDelayMs(uint32_t node1, uint32_t node2) +{ + // get the distance difference between node1 and node2 + double distance_1, distance_2 = 0; + double diff = 0; + // // Ptr mob = m_device->GetNode()->GetObject(); + // Ptr mob1 = m_device->GetChannel()->GetDevice(node1)->GetNode()->GetObject(); + // Ptr mob2 = m_device->GetChannel()->GetDevice(node1)->GetNode()->GetObject(); + // distance_diff = mob1->GetDistanceFrom() + + // Get the distance from the mobility model + // Ptr sObject = m_device->GetNode(); + Ptr sObject1 = m_device->GetChannel()->GetDevice(node1)->GetNode(); + Ptr senderModel1 = sObject1->GetObject (); + + Ptr sObject2 = m_device->GetChannel()->GetDevice(node2)->GetNode(); + Ptr senderModel2 = sObject2->GetObject (); + + Ptr rObject = m_device->GetNode(); + Ptr recvModel = rObject->GetObject (); + + distance_1 = senderModel1->GetDistanceFrom(recvModel); + distance_2 = senderModel2->GetDistanceFrom(recvModel); + diff = distance_1 - distance_2; + + // std::cout << "Distance Diff: " << distance_diff << "\n"; + // std::cout << "Distance Delay: " << (int) distance_diff*1000/1500 << "\n"; + uint16_t d = (int) diff*1000/1500; + if ((GetTxTime(m_packetSize).GetMilliSeconds() - d) < 0) + { + return 0; + } + else + { + return d; + } +} + +// calculate energy needed for a direct transmission +double +AquaSimJammingMac::calcEnergy(double l) +{ + /* + Corresponding MATLAB code: + + l=distance; % distance in km + f=25; % frequency in kHz + k=2; % spreading factor 1<=k<=2 + Ldata=100; % packet size, in bytes + R=10000; % acoustic rate, in bps + Prx=0.82; % node power consumption for receiving and processing, in watts (WHOI modem is 0.82 for FSK) + Ptmax=60; % max transmission power, in watts (WHOI modem is 60 watts at 25khz) + Trx=Ldata*8/R; % seconds to receive a packet, make it equal to transmission delay + Prmin=5.4764*10^(-8); % threshold for receiving a signal + % 1.8255*10^(-8): 20W at 3km + % 5.4764*10^(-8): 60W at 3km + Tpreamp=Trx; % time to receive and decode the preamp so that + % the nodes know what the destination of packet is + % in seconds + % 0.150 is the preample of UCONN OFDM modem + C1=(0.011*f^2/(1+f^2)+44*f^2/(4100+f^2)+2.75/10000*f^2+0.003)*l; + C2=Prmin*(l*1000)^k*8*Ldata/R; + C3=Trx*Prx; % energy consumptions for receiving + result=C2*10^(C1)+C3; % energy consumption of a direct transmission */ + + l = l/1000.; // in kilometers + + double f = 25; + uint8_t k = 2; + uint32_t Ldata = 100; + uint32_t R = 10000; + double Prx = 0.82; + // double Ptmax = 60; + double Trx = Ldata*8/R; + double Prmin = 5.4764 * std::pow(10, -8); + // double Tpreamp = Trx; + + double C1 = (0.011*std::pow(f,2)/(1+std::pow(f,2))+44*std::pow(f,2)/(4100+std::pow(f,2))+2.75/10000*std::pow(f,2)+0.003)*l; + double C2 = Prmin*std::pow(l*1000,k)*8*Ldata/R; + double C3 = Trx*Prx; + + double result = C2*std::pow(10,C1)+C3; + + return result; +} + +void +AquaSimJammingMac::calcTotalEnergy(uint32_t node1, uint32_t node2) +{ + // get the distance difference between node1 and node2 + double distance_1, distance_2 = 0; + + // Get the distance from the mobility model + Ptr sObject1 = m_device->GetChannel()->GetDevice(node1)->GetNode(); + Ptr senderModel1 = sObject1->GetObject (); + + Ptr sObject2 = m_device->GetChannel()->GetDevice(node2)->GetNode(); + Ptr senderModel2 = sObject2->GetObject (); + + Ptr rObject = m_device->GetNode(); + Ptr recvModel = rObject->GetObject (); + + distance_1 = senderModel1->GetDistanceFrom(recvModel); + distance_2 = senderModel2->GetDistanceFrom(recvModel); + + double totalEnergy = calcEnergy(distance_1) + calcEnergy(distance_2); + + m_energy_list.push_back(totalEnergy); + m_trace_energy(totalEnergy); + + // calculate energy consumption as well + double energy_sum = 0; + for(uint32_t i=0; i < m_energy_list.size(); i++) + { + energy_sum = energy_sum + m_energy_list[i]; + } + // std::cout << "ENERGY SUM: " << energy_sum << "\n"; +} + +// calculate vulnerable area for a pair of nodes +void +AquaSimJammingMac::calcVulnerableArea(uint32_t node1, uint32_t node2) +{ + // get the distance difference between node1 and node2 + double D1R, D2R, D12 = 0; + + // Get the distance from the mobility model + Ptr sObject1 = m_device->GetChannel()->GetDevice(node1)->GetNode(); + Ptr senderModel1 = sObject1->GetObject (); + + Ptr sObject2 = m_device->GetChannel()->GetDevice(node2)->GetNode(); + Ptr senderModel2 = sObject2->GetObject (); + + Ptr rObject = m_device->GetNode(); + Ptr recvModel = rObject->GetObject (); + + D1R = senderModel1->GetDistanceFrom(recvModel); + D2R = senderModel2->GetDistanceFrom(recvModel); + + D12 = senderModel1->GetDistanceFrom(senderModel2); + + double TH = D1R / 1500. + 0.08 - D2R/1500.; + + if (D1R > D2R) + { + m_trace_area(std::pow(500, 3)); + return; + } + + // std::cout << "FOO: " << (double)hold_time_ms/1000. << "\n"; + double mu1 = 1500 * (0.08 - TH); + double mu2 = 1500 * (0.08 + TH); + + double result1 = 3.14*mu1*(std::pow(D12,2) - std::pow(mu1,2))/8 * ( std::pow((2*500-mu1), 3) / (3*std::pow(D12,3)) + 2/3 - (2*500-mu1)/D12 ); + if (result1<0) + { + result1=0; + } + + double result2 = 3.14*mu2*(std::pow(D12,2) - std::pow(mu2,2))/8 * ( std::pow((2*500-mu2), 3) / (3*std::pow(D12,3)) + 2/3 - (2*500-mu2)/D12 ); + if (result2<0) + { + result2=0; + } + + // store the result in the vector + m_vulnerable_list.push_back(result1 + result2); + m_trace_area(result1 + result2); + + // print the intermediate average value + double sum = 0; + for(uint32_t i=0; i < m_vulnerable_list.size(); i++) + { + sum = sum + m_vulnerable_list[i]; + } + // std::cout << "AREA MEAN: " << sum / m_vulnerable_list.size() << "\n"; + // std::cout << "AREA SUM: " << sum << "\n"; +} + +void +AquaSimJammingMac::TriggerCsReply() +{ + if (m_request_list.size() != 0) + { + // Create a schedule based on a list of the requesting nodes + std::map schedule = CreateSchedule(TRAIN_SCHEDULE); + // std::map schedule = CreateSchedule(AUCTION_SCHEDULE); + // std::map schedule = CreateSchedule(RANDOM_SCHEDULE); + + // Generate and broadcats the schedule (CS-reply) back + Ptr cs_reply = GenerateCsReply(schedule); + // Broadcast the packet using Aloha + // Simulator::Schedule(Seconds(GetBackoff()), &AquaSimJammingMac::SendPacketAloha, this, cs_reply->Copy()); + SendPacketAloha(cs_reply); + + m_scheduled_pkts(m_request_list.size()); + + // Clear the cc-request list + m_request_list.clear(); + } + + // set the next timer at the sink-node to broadcast the schedule + Simulator::Schedule(m_cs_delay + m_data_delay + m_epoch_time, &AquaSimJammingMac::TriggerCsReply, this); +} + +std::map +AquaSimJammingMac::CreateSchedule(JammingMacScheduleType schedule_type) +{ + std::map schedule; + if (schedule_type == TRAIN_SCHEDULE) + { + // if we have an odd number of requests, we need to remove one since the auction algorithm takes only even number of coordinates + if ((m_request_list.size() % 2) != 0) + { + m_request_list.erase(std::prev(m_request_list.end())); + } + // std::cout << "LIST SIZE: " << m_request_list.size() << "\n"; + + // Iterate over all the cc-requests and schedule their transmissions side-by-side + uint16_t delay_ms = 0; + uint16_t pkt_tx_time_ms = GetTxTime(m_packetSize).GetMilliSeconds(); + // std::cout << "LIST SIZE: " << m_request_list.size() << "\n"; + uint32_t n, node1, node2 = 0; + + n = 0; + for (auto const& x : m_request_list) + { + schedule.insert(std::make_pair(x.first, delay_ms)); + delay_ms += (pkt_tx_time_ms + m_guard_time.GetMilliSeconds()); + + if (n == 0) + { + node1 = x.first; + // std::cout << "node 1: " << x.first << "\n"; + n++; + } + else + { + node2 = x.first; + n = 0; + // std::cout << "node 2: " << x.first << "\n"; + // calc vulnerable area + calcVulnerableArea(node1, node2); + } + // calc energy + calcTotalEnergy(node1, node2); + + } + } + else if (schedule_type == RANDOM_SCHEDULE) + { + + // if we have an odd number of requests, we need to remove one since the auction algorithm takes only even number of coordinates + if ((m_request_list.size() % 2) != 0) + { + m_request_list.erase(std::prev(m_request_list.end())); + } + // std::cout << "LIST SIZE: " << m_request_list.size() << "\n"; + + // Iterate over all the cc-requests and schedule their transmissions side-by-side + uint16_t delay_ms = 0; + uint16_t pkt_tx_time_ms = GetTxTime(m_packetSize).GetMilliSeconds(); + // std::cout << "LIST SIZE: " << m_request_list.size() << "\n"; + uint32_t n, node1, node2 = 0; + + // generate random node sequence + // std::map node_seq; + std::list node_seq; + std::vector requested_node_ids; + for (auto const& x : m_request_list) + { + requested_node_ids.push_back(x.first); + } + + uint32_t node_index = m_rand->GetInteger(0, requested_node_ids.size()-1); + // node_seq.insert(std::make_pair(node_index, 0)); + node_seq.push_back(requested_node_ids.at(node_index)); + for (uint32_t i = 0; i < requested_node_ids.size()-1; i++) + { + // while (node_seq.find(node_index) != node_seq.end()) + while (std::find(node_seq.begin(), node_seq.end(), requested_node_ids.at(node_index)) != node_seq.end()) + { + node_index = m_rand->GetInteger(0, requested_node_ids.size()-1); + } + // node_seq.insert(std::make_pair(node_index, node_index)); + node_seq.push_back(requested_node_ids.at(node_index)); + } + + n = 0; + // for (auto const& x : m_request_list) + // std::cout << "Schedule:\n"; + for (auto const& x : node_seq) + { + // schedule.insert(std::make_pair(x.first, delay_ms)); + schedule.insert(std::make_pair(x, delay_ms)); + delay_ms += (pkt_tx_time_ms + m_guard_time.GetMilliSeconds()); + + if (n == 0) + { + node1 = x; + // std::cout << "node 1: " << x.first << "\n"; + n++; + } + else + { + node2 = x; + n = 0; + // std::cout << "node 2: " << x.first << "\n"; + // std::cout << "node 1: " << node1 << ", node 2: " << node2 << "\n"; + // calc vulnerable area + calcVulnerableArea(node1, node2); + } + // calc energy + calcTotalEnergy(node1, node2); + } + + + } + else if (schedule_type == AUCTION_SCHEDULE) + { + NS_FATAL_ERROR ("Auction schedule is currently not supported. Please refer to Matlab code."); + // // if we have an odd number of requests, we need to remove one since the auction algorithm takes only even number of coordinates + // if ((m_request_list.size() % 2) != 0) + // { + // m_request_list.erase(std::prev(m_request_list.end())); + // } + // // std::cout << "LIST SIZE: " << m_request_list.size() << "\n"; + + // // prepare the input coords.txt file for matlab binary + // std::ofstream input_file("/home/ubuntu/simulation2/coords.txt"); + + // input_file << m_request_list.size() << "\n"; + // std::map node_id_map; // maintain a map between real node ids and the ones returned by the assignemnt matrix + // uint32_t matrix_node_id = 0; + // for (auto const& entry : m_request_list) + // { + // input_file << entry.second.x << "\n"; + // input_file << entry.second.z << "\n"; + // input_file << entry.second.y << "\n"; + // node_id_map.insert(std::make_pair(matrix_node_id, entry.first)); // matrix_node_id : real_node_id + // matrix_node_id++; + // } + // input_file.close(); + + // // trigger the binary file from Matlab + // std::string str = "/home/ubuntu/simulation2/main > schedule.txt"; + // const char *command = str.c_str(); + // std::system(command); + + // // read schedule from file + // char read_el[SIZE]; + // FILE *fp=fopen("schedule.txt", "r"); + + // if(fp == NULL){ + // printf("File Opening Error!!"); + + // } + // fgets(read_el, SIZE, fp); + // fclose(fp); + // // std::cout << "SCHEDULE: " << read_el << "\n"; + + // std::map pairs = getPairsFromFile(m_request_list.size(), read_el); + + // // prepare a schedule based on given pairs + // uint16_t delay_ms = 0; + // uint16_t pkt_tx_time_ms = GetTxTime(m_packetSize).GetMilliSeconds(); + // // std::cout << "PAIR SIZE: " << pairs.size() << "\n"; + // for (auto const& x : pairs) + // { + // // schedule.insert(std::make_pair(x.first, delay_ms)); + // // delay_ms += (pkt_tx_time_ms + m_guard_time.GetMilliSeconds()); + // uint16_t dist_delay_ms = getDistanceDelayMs(node_id_map.find(x.first)->second, node_id_map.find(x.second)->second); + + // // std::cout << "NODE_1: " << node_id_map.find(x.first)->second << "\n"; + // schedule.insert(std::make_pair(node_id_map.find(x.first)->second, delay_ms)); + // delay_ms += (pkt_tx_time_ms - dist_delay_ms + m_guard_time.GetMilliSeconds()); + + // // std::cout << "NODE_2: " << node_id_map.find(x.second)->second << "\n"; + // schedule.insert(std::make_pair(node_id_map.find(x.second)->second, delay_ms)); + // // delay_ms += (pkt_tx_time_ms - dist_delay_ms + m_guard_time.GetMilliSeconds()); + // delay_ms += (pkt_tx_time_ms + m_guard_time.GetMilliSeconds()); // add just a tx_time of a packet in-between the pairs + + // // calc vulnerable area + // calcVulnerableArea(node_id_map.find(x.first)->second, node_id_map.find(x.second)->second); + + // // calc energy + // calcTotalEnergy(node_id_map.find(x.first)->second, node_id_map.find(x.second)->second); + // } + } + else + { + NS_FATAL_ERROR ("Invalid schedule type!"); + } + return schedule; +} + +Ptr +AquaSimJammingMac::GenerateCsReply(std::map schedule) +{ + // Create a packet and allocate the corresponding headers + AquaSimHeader ash; + MacHeader mach; + JammingMacHeader jamh; + + // Src/dst addresses + AquaSimAddress src = AquaSimAddress::ConvertFrom(GetAddress()); // sink-node address + AquaSimAddress dst = AquaSimAddress::GetBroadcast(); // broadcast cs-reply to all the nodes + + Ptr cs_reply = Create(); + ash.SetSAddr(src); + ash.SetDAddr(dst); + ash.SetDirection(AquaSimHeader::DOWN); + mach.SetSA(src); + mach.SetDA(dst); + mach.SetDemuxPType(MacHeader::UWPTYPE_OTHER); + // Set schedule + JammingMacHeader cs_reply_h; + cs_reply_h.SetPType(2); // 2 - cs-reply + cs_reply_h.SetNodeId(m_device->GetNode()->GetId()); + // Allocate the schedule + for (auto const& x : schedule) + { + cs_reply_h.SetSchedule(x.first, x.second); + } + cs_reply->AddHeader(cs_reply_h); + cs_reply->AddHeader(mach); + cs_reply->AddHeader(ash); + return cs_reply; +} + +void +AquaSimJammingMac::ProcessCcRequest(uint32_t node_id, Vector coords) +{ + // Store the node info in a request list + if (m_request_list.find(node_id) == m_request_list.end()) + { + // Insert a new entry in a map + m_request_list.insert(std::make_pair(node_id, coords)); + } +} + +bool +AquaSimJammingMac::RecvProcess (Ptr pkt) +{ + NS_LOG_FUNCTION(this); + + AquaSimHeader ash; + MacHeader mach; + JammingMacHeader jamh; + pkt->RemoveHeader(ash); + pkt->RemoveHeader(mach); + pkt->RemoveHeader(jamh); + AquaSimAddress dst = mach.GetDA(); + AquaSimAddress src = mach.GetSA(); + + + if (ash.GetErrorFlag()) + { + NS_LOG_DEBUG("JammingMac:RecvProcess: received corrupt packet."); + pkt=0; + return false; + } + + // std::cout << "Node ID: " << m_device->GetNode()->GetId() << "\n"; + // std::cout << "SRC ADDR: " << mach.GetSA() << "\n"; + // std::cout << "DST ADDR: " << mach.GetDA() << "\n"; + + // If the destination unicast, then process either cc-request or data-packet + // TODO: assert that this is actually a sink + if (dst == AquaSimAddress::ConvertFrom(m_device->GetAddress())) + { + // Check the received packet type + // If the packet is CC-request (type == 1) + if (jamh.GetPType() == 1) + { + // std::cout << "Time_ms: " << Simulator::Now().GetMilliSeconds() << "\tSink:" << m_device->GetNode()->GetId() << "\tCC-request received from Node " << +jamh.GetNodeId() << "\n"; + // Handle CC-request on a sink + // if (!m_sink_timer.IsRunning()) + // { + // // If the timer is not running, start it and set the expiration time + // m_sink_timer.SetFunction(&AquaSimJammingMac::TriggerCsReply, this); + // m_sink_timer.SetDelay(m_cc_accumulation_time); + // m_sink_timer.Schedule(); + // } + + // Accumulate all incoming cc-requests while the timer runs + // Vector coords = Vector(0, 0, 0); + Vector coords = jamh.GetCoordinates(); + // std::cout << "RECVD COORDS: " << coords << "\n"; + ProcessCcRequest(jamh.GetNodeId(), coords); + } + else if (jamh.GetPType() == 0) + { + // std::cout << "Time_ms: " << Simulator::Now().GetMilliSeconds() << "\tSink:" << m_device->GetNode()->GetId() << "\tData-packet received from Node " << +jamh.GetNodeId() << "\n"; + + // trace the E2E delay + AquaSimTimeTag timeTag; + pkt->RemovePacketTag(timeTag); + m_e2eDelayTrace((Simulator::Now() - timeTag.GetTime()).GetMilliSeconds()); + + // std::cout << (Simulator::Now() - timeTag.GetTime()).GetMilliSeconds() << "\n"; + + // Attach aqua-sim-header and send packet up + pkt->AddHeader(ash); + SendUp(pkt); + // trace a data packet reception event + m_recv_data_pkts(); + } + } + // If the destination is broadcast, then the packet is cs-reply, process it accordingly + else if (dst == AquaSimAddress::GetBroadcast()) + { + if (jamh.GetPType() == 2) + { + // std::cout << "Time_ms: " << Simulator::Now().GetMilliSeconds() << "\tNode:" << m_device->GetNode()->GetId() << "\tCS-reply received from Node " << +jamh.GetNodeId() << "\n"; + + // save the schedule and then send data packets during the DATA-send phase + m_current_schedule = jamh; + + // // Schedule packet transmissions based on the schedule + // if (jamh.GetNodeBit(m_device->GetNode()->GetId()) == 1) + // { + // Time tx_delay = MilliSeconds(jamh.GetSchedule(m_device->GetNode()->GetId())); + // // Get data packet from queue + // Ptr data_packet = m_sendQueue.front(); + // m_sendQueue.pop_front(); + // // Attach mac-headers to packet + // data_packet->RemoveHeader(ash); + // mach.SetSA(ash.GetSAddr()); + // mach.SetDA(ash.GetDAddr()); + // mach.SetDemuxPType(MacHeader::UWPTYPE_OTHER); + // JammingMacHeader data_h; + // data_h.SetPType(0); // 0 - data packet + // data_h.SetNodeId(m_device->GetNode()->GetId()); + // data_packet->AddHeader(data_h); + // data_packet->AddHeader(mach); + // data_packet->AddHeader(ash); + + // Simulator::Schedule(tx_delay, &AquaSimJammingMac::SendDown, this, data_packet, ns3::NIDLE); + // } + } + else + { + NS_FATAL_ERROR ("The broadcast-packet is not CS-reply!"); + } + } + else + { + // TODO: + // log the transmission, overheard from the other nodes to another destination + } + + pkt=0; + return false; +} + +bool +AquaSimJammingMac::TxProcess(Ptr pkt) +{ + NS_LOG_FUNCTION(this << pkt); + + // Attach a timestamp tag to calculate E2E delay + AquaSimTimeTag timeTag; + timeTag.SetTime(Simulator::Now()); + pkt->AddPacketTag(timeTag); + + // Put incoming data packets to send-queue + m_sendQueue.push_back(pkt); + + // Trace the current queue size + m_queueSizeTrace(m_sendQueue.size()); + + // // Start the very first Channel Competition (CC) phase + // if (!m_firstCcInit) + // { + // StartCC(); + // m_firstCcInit = true; + // } + + return true; +} + +void AquaSimJammingMac::DoDispose() +{ + NS_LOG_FUNCTION(this); + AquaSimMac::DoDispose(); +} diff --git a/model/aqua-sim-mac-jmac.h b/model/aqua-sim-mac-jmac.h new file mode 100644 index 0000000..bad3c46 --- /dev/null +++ b/model/aqua-sim-mac-jmac.h @@ -0,0 +1,125 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2020 The City University of New York + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Dmitrii Dugaev + */ + +#ifndef AQUA_SIM_MAC_JMAC_H +#define AQUA_SIM_MAC_JMAC_H + +#include "aqua-sim-mac.h" +#include "aqua-sim-time-tag.h" +#include "aqua-sim-header.h" +#include "aqua-sim-header-mac.h" + +namespace ns3 { + +/** + * \ingroup aqua-sim-ng + * + * \brief Jamming MAC using basic backoff mechanism + */ +enum JammingMacScheduleType{ + TRAIN_SCHEDULE, // schedule packet transmissions "side-by-side" so they will arrive to a sink as a "train" of packets + RANDOM_SCHEDULE, // randomly allocate packet transmissions + AUCTION_SCHEDULE // use Auction algorithm from the paper +}; + +class AquaSimJammingMac : public AquaSimMac +{ +public: + AquaSimJammingMac(); + int64_t AssignStreams (int64_t stream); + + static TypeId GetTypeId(void); + + // Handle a packet coming from channel + virtual bool RecvProcess (Ptr); + // Handle a packet coming from upper layers + virtual bool TxProcess (Ptr); + + // Start Channel-Competition request + void StartCC(); + // Start Data-send phase + void StartData(); + // Send a packet using pure ALOHA channel access + void SendPacketAloha(Ptr p); + // Return a backoff value for ALOHA + double GetBackoff(); + // Assemble all received cc-requests and send a cs-reply + void TriggerCsReply(); + // Process incoming CC-request - obtain node info and store it + void ProcessCcRequest(uint32_t node_id, Vector coords); + // Create a schedule based on the received requests + // returns: : + std::map CreateSchedule(JammingMacScheduleType schedule_type); + // Return a cs-reply packet based on a given schedule + Ptr GenerateCsReply(std::map schedule); + + // get pairs from txt file + std::map getPairsFromFile(uint32_t nNodes, char matrixString[]); + // get distance-delay between two nodes in milliseconds + uint16_t getDistanceDelayMs(uint32_t node1, uint32_t node2); + // calculate vulnerable area for a pair of nodes + void calcVulnerableArea(uint32_t node1, uint32_t node2); + // calculate energy needed for a direct transmission + double calcEnergy(double dist); + void calcTotalEnergy(uint32_t node1, uint32_t node2); + + // initialize the cycle at the beginning + void initCycle(); + +protected: + virtual void DoDispose(); + +private: + Ptr m_rand; + uint16_t m_packetSize; + std::list> m_sendQueue; + + // Interval between cc-requests at the sender side (nodes) + Time m_epoch_time; + // Store the incoming cc-requests in a map: : + std::map m_request_list; + // Guard interval in-between packets in a train + Time m_guard_time; + // Keep track of a number of cc-->cs-->data phases + uint32_t m_epoch_no = 0; + + // store vulnerable area values + std::vector m_vulnerable_list; + std::vector m_energy_list; + + // this is a delay of a CS-reply sent from sink to sensor-nodes + Time m_cs_delay; + // this is a delay to accomodate DATA-packets transmissions from sensor-nodes to a sink + Time m_data_delay = Seconds(10); + + // store the last schedule received from the sink + JammingMacHeader m_current_schedule; + + // tracebacks to count the stats + TracedCallback m_trace_area; + TracedCallback m_trace_energy; + TracedCallback m_scheduled_pkts; + TracedCallback<> m_recv_data_pkts; + +}; // class AquaSimJammingMac + +} // namespace ns3 + +#endif /* AQUA_SIM_MAC_JMAC_H */ diff --git a/model/aqua-sim-mac-tdma.cc b/model/aqua-sim-mac-tdma.cc new file mode 100644 index 0000000..a9f1c0e --- /dev/null +++ b/model/aqua-sim-mac-tdma.cc @@ -0,0 +1,217 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2021 The City University of New York + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Dmitrii Dugaev + */ + +#include "aqua-sim-mac-tdma.h" +#include "aqua-sim-header.h" +#include "aqua-sim-header-mac.h" +#include "aqua-sim-address.h" +#include "aqua-sim-time-tag.h" + +#include "ns3/log.h" +#include "ns3/integer.h" +#include "ns3/simulator.h" + +#include + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE("AquaSimTdmaMac"); +NS_OBJECT_ENSURE_REGISTERED(AquaSimTdmaMac); + + +/* ========= +TDMA-MAC +============ */ + +AquaSimTdmaMac::AquaSimTdmaMac() +{ + m_rand = CreateObject (); + + // Tdma + m_tdma_state = IDLE; + m_tdma_slot_period = 10; + m_tdma_slot_ms = MilliSeconds(800); // TODO: adjust it to accomodate the max possible Tx range, pkt size and channel speed + m_tdma_guard_interval_ms = MilliSeconds(1); + m_tdma_slot_number = 0; + +} + +TypeId +AquaSimTdmaMac::GetTypeId() +{ + static TypeId tid = TypeId("ns3::AquaSimTdmaMac") + .SetParent() + .AddConstructor() + .AddAttribute ("TdmaSlotPeriod", "Number of slots in a single TDMA round", UintegerValue(10), + MakeUintegerAccessor (&AquaSimTdmaMac::m_tdma_slot_period), MakeUintegerChecker ()) + .AddAttribute ("TdmaSlotNumber", "TDMA Tx slot number of the node", UintegerValue(0), + MakeUintegerAccessor (&AquaSimTdmaMac::m_tdma_slot_number), MakeUintegerChecker ()) + .AddAttribute ("TdmaSlotDuration", "Duration of TDMA slot", TimeValue(MilliSeconds(800)), + MakeTimeAccessor (&AquaSimTdmaMac::m_tdma_slot_ms), MakeTimeChecker()) + .AddAttribute ("TdmaGuardTime", "Guard time in-between two slots", TimeValue(MilliSeconds(1)), + MakeTimeAccessor (&AquaSimTdmaMac::m_tdma_guard_interval_ms), MakeTimeChecker()) + ; + return tid; +} + +int64_t +AquaSimTdmaMac::AssignStreams (int64_t stream) +{ + NS_LOG_FUNCTION (this << stream); + m_rand->SetStream(stream); + return 1; +} + +bool +AquaSimTdmaMac::RecvProcess (Ptr pkt) +{ + NS_LOG_FUNCTION(this); + + AquaSimHeader ash; + MacHeader mach; + pkt->RemoveHeader(ash); + pkt->RemoveHeader(mach); + AquaSimAddress dst = mach.GetDA(); + AquaSimAddress src = mach.GetSA(); + + if (ash.GetErrorFlag()) + { + NS_LOG_DEBUG("TdmaMac:RecvProcess: received corrupt packet."); + pkt=0; + return false; + } + + // read the next sender id + if (AquaSimAddress::ConvertFrom(m_device->GetAddress()) == dst) + { + // trace the E2E delay + AquaSimTimeTag timeTag; + pkt->RemovePacketTag(timeTag); + m_e2eDelayTrace((Simulator::Now() - timeTag.GetTime()).GetMilliSeconds()); + + pkt->AddHeader(ash); + SendUp(pkt); + } + + return false; +} + +bool +AquaSimTdmaMac::TxProcess(Ptr pkt) +{ + NS_LOG_FUNCTION(this << pkt); + + // std::cout << m_device->GetNode()->GetId() << " " << m_tdma_slot_number << "\n"; + + // Attach a timestamp tag to calculate E2E delay + AquaSimTimeTag timeTag; + timeTag.SetTime(Simulator::Now()); + pkt->AddPacketTag(timeTag); + + // Put incoming data packets to send-queue + m_send_queue.push_back(pkt); + m_queueSizeTrace(m_send_queue.size()); + + // Check at which state the GCSMA MAC is now (IDLE, IFS or backoffs) + if ((m_tdma_state == IDLE)) + { + // Start TDMA transmission + StartTdma(); + } + + return true; +} + +void +AquaSimTdmaMac::StartTdma() +{ + // If queue is empty, set TDMA state back to IDLE and return + if (m_send_queue.size() == 0) + { + m_tdma_state = IDLE; + return; + } + + // Set TDMA state to TX + m_tdma_state = TX; + + // Get packet from queue + Ptr p = m_send_queue.front(); + m_send_queue.pop_front (); + + m_packet_start_ts = Simulator::Now(); // to calculate MAC send delay + // Schedule packet for Tx within the own slot, according to TDMA schedule + ScheduleNextSlotTx(p); +} + +void +AquaSimTdmaMac::SendPacket (Ptr packet) +{ + AquaSimHeader ash; + MacHeader mach; + + packet->RemoveHeader(ash); + mach.SetSA(ash.GetSAddr()); + mach.SetDA(ash.GetDAddr()); + mach.SetDemuxPType(MacHeader::UWPTYPE_OTHER); + + packet->AddHeader(mach); + packet->AddHeader(ash); + + // Just send packet down to PHY + SendDown(packet); + // Go to next packet in the queue + StartTdma(); +} + +void +AquaSimTdmaMac::ScheduleNextSlotTx(Ptr packet) +{ + // Get current slot + uint32_t current_slot = Simulator::Now().GetMicroSeconds() / (m_tdma_slot_ms + m_tdma_guard_interval_ms).GetMicroSeconds(); + + // Iterate over a single TDMA-round to find next-slot transmission + uint32_t next_slot = 0; + for (uint32_t i=(current_slot+1); i<(current_slot+1+m_tdma_slot_period); i++) + { + if ((i%m_tdma_slot_period) == m_tdma_slot_number) + { + next_slot = i; + } + } + if (next_slot == 0) + { + NS_FATAL_ERROR ("Couldn't find next TDMA slot!"); + } + if (next_slot < current_slot) + { + NS_FATAL_ERROR ("Next slot is smaller than current slot!"); + } + + // Schedule the packet transmission to that slot + Simulator::Schedule(next_slot * (m_tdma_slot_ms + m_tdma_guard_interval_ms) - Simulator::Now(), &AquaSimTdmaMac::SendPacket, this, packet->Copy()); +} + +void +AquaSimTdmaMac::DoDispose() +{ + NS_LOG_FUNCTION(this); + AquaSimMac::DoDispose(); +} diff --git a/model/aqua-sim-mac-tdma.h b/model/aqua-sim-mac-tdma.h new file mode 100644 index 0000000..8e1faaf --- /dev/null +++ b/model/aqua-sim-mac-tdma.h @@ -0,0 +1,81 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2021 The City University of New York + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Dmitrii Dugaev + */ + +#ifndef AQUA_SIM_MAC_TDMA_H +#define AQUA_SIM_MAC_TDMA_H + +#include "aqua-sim-mac.h" +#include "aqua-sim-header.h" +#include "aqua-sim-header-mac.h" + +namespace ns3 { + +/** + * \ingroup aqua-sim-ng + * + * \brief Classic TDMA MAC implementation + */ + +class AquaSimTdmaMac : public AquaSimMac +{ +public: + + typedef enum { + IDLE, TX + } TdmaState; + + AquaSimTdmaMac(); + int64_t AssignStreams (int64_t stream); + + static TypeId GetTypeId(void); + + // Handle a packet coming from channel + virtual bool RecvProcess (Ptr); + // Handle a packet coming from upper layers + virtual bool TxProcess (Ptr); + + void SendPacket (Ptr packet); + // TDMA methods + void StartTdma(); + // schedule the transmission in the next available slot + void ScheduleNextSlotTx(Ptr packet); + +protected: + virtual void DoDispose(); + +private: + Ptr m_rand; + uint16_t m_packetSize; + + // TDMA-related variables + TdmaState m_tdma_state; + Time m_packet_start_ts = Seconds(0); + std::list> m_send_queue; + + uint16_t m_tdma_slot_period; // how many slots in 1 TDMA round + Time m_tdma_slot_ms; + Time m_tdma_guard_interval_ms; + uint32_t m_tdma_slot_number; // store the TDMA slot Tx number of this node + +}; // class AquaSimTdmaMac + +} // namespace ns3 + +#endif /* AQUA_SIM_MAC_TDMA_H */ diff --git a/model/aqua-sim-mac-trumac.cc b/model/aqua-sim-mac-trumac.cc new file mode 100644 index 0000000..b2e84c3 --- /dev/null +++ b/model/aqua-sim-mac-trumac.cc @@ -0,0 +1,458 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2021 The City University of New York + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Dmitrii Dugaev + */ + +#include "aqua-sim-mac-trumac.h" +#include "aqua-sim-header.h" +#include "aqua-sim-header-mac.h" +#include "aqua-sim-address.h" +#include "aqua-sim-time-tag.h" + +#include "ns3/log.h" +#include "ns3/integer.h" +#include "ns3/simulator.h" + +#include + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE("AquaSimTrumac"); +NS_OBJECT_ENSURE_REGISTERED(AquaSimTrumac); + + +/* ========= +TR-MAC +============ */ + +AquaSimTrumac::AquaSimTrumac() +{ + m_rand = CreateObject (); + m_packetSize = 800; // bytes + m_guard_time = MilliSeconds(1); + + // schedule the very first TR-cycle, if node ID is the first one in the sequence + Simulator::Schedule(Seconds(1), &AquaSimTrumac::initCycle, this); + + Simulator::Schedule(m_contention_timeout, &AquaSimTrumac::initDataContention, this); +} + +TypeId +AquaSimTrumac::GetTypeId() +{ + static TypeId tid = TypeId("ns3::AquaSimTrumac") + .SetParent() + .AddConstructor() + .AddAttribute("PacketSize", "Size of packet", + IntegerValue(800), + MakeIntegerAccessor (&AquaSimTrumac::m_packetSize), + MakeIntegerChecker ()) + .AddAttribute ("StartNodeId", "ID of a starting node in TR-cycle", + UintegerValue(0), + MakeUintegerAccessor(&AquaSimTrumac::m_start_node_id), + MakeUintegerChecker ()) + .AddAttribute ("TotalNodes", "Total number of nodes in a swarm", + UintegerValue(0), + MakeUintegerAccessor(&AquaSimTrumac::m_total_nodes), + MakeUintegerChecker ()) + .AddAttribute ("GuardTime", "Interval between reception and sending the next packet", + TimeValue(MilliSeconds(1)), + MakeTimeAccessor(&AquaSimTrumac::m_guard_time), + MakeTimeChecker()) + .AddAttribute ("ContentionTimeout", "Start contention-based transmission if no traffic have been heard within the contention timeout", + TimeValue(Seconds(5)), + MakeTimeAccessor(&AquaSimTrumac::m_contention_timeout), + MakeTimeChecker()) + .AddAttribute ("AlgoId", "Select between different next-hop selection algorithms", + UintegerValue(0), // 0 - random; 1 - optimal (nearest-neighbor) + MakeUintegerAccessor(&AquaSimTrumac::m_algo_id), + MakeUintegerChecker ()) + ; + return tid; +} + +int64_t +AquaSimTrumac::AssignStreams (int64_t stream) +{ + NS_LOG_FUNCTION (this << stream); + m_rand->SetStream(stream); + return 1; +} + +void +AquaSimTrumac::initCycle() +{ + if (m_total_nodes == 0) + { + NS_FATAL_ERROR ("Invalid total number of nodes!"); + } + + // if (m_device->GetNode()->GetId() == 0) + // { + // Ptr senderModel = m_device->GetNode()->GetObject(); + // std::cout << senderModel->GetPosition().x << "\n"; + // } + + // populate the global list of nodes' coordinates + if (m_graph.size() == 0) + { + double dist = 0; + for (uint32_t i = 0; i < m_total_nodes; i++) + { + for (uint32_t j = 0; j < m_total_nodes; j++) + { + // if (i < j) + if (i != j) + { + Ptr senderModel = m_device->GetChannel()->GetDevice(i)->GetNode()->GetObject(); + Ptr recvModel = m_device->GetChannel()->GetDevice(j)->GetNode()->GetObject(); + dist = senderModel->GetDistanceFrom(recvModel); + m_graph.insert(std::make_pair(std::make_pair(i,j), dist)); + } + } + } + // store the sub-optimal greedy (nearest neighbor) schedule + m_schedule = runNearestNeighborTSP(); + } + // else + // { + // // std::cout << m_graph.size() << "\n"; + // std::vector schedule = runNearestNeighborTSP(); + // // for (uint32_t i=0; ifirst.first << "," << it->first.second << ": " << it->second << "\n"; + // // } + + // } + + if (m_device->GetNode()->GetId() == m_start_node_id) + { + // std::cout << Simulator::Now().GetMilliSeconds() << " Starting TR-Cycle\n"; + + // check if node ID is the starting one and send the very first packet/token, if yes + m_heard_node_ids.clear(); + SendPacket(); + } +} + +void +AquaSimTrumac::initDataContention() +{ + if ((Simulator::Now() - m_lastTxStamp) >= m_contention_timeout) + { + // std::cout << Simulator::Now().GetMilliSeconds() << ": " << m_device->GetNode()->GetId() << "\n"; + SendPacket(); + // Simulator::Schedule(m_contention_timeout, &AquaSimTrumac::initDataContention, this); + } + // else + // { + // Simulator::Schedule(m_contention_timeout - (Simulator::Now() - m_lastTxStamp), &AquaSimTrumac::initDataContention, this); + // } + Simulator::Schedule(Seconds(m_rand->GetInteger(5, 10)), &AquaSimTrumac::initDataContention, this); +} + +std::vector +AquaSimTrumac::runNearestNeighborTSP() +{ + std::vector optimalSchedule; + std::vector currentSchedule; + double optimalSum = 100000000; + double currentSum = 0; + + for (uint32_t n=0; nfirst.first == nextNode) + { + if (std::find(currentSchedule.begin(), currentSchedule.end(), it->first.second) == currentSchedule.end()) + { + if (it->second < w) + { + w = it->second; + currentBestNode = it->first.second; + } + } + } + } + nextNode = currentBestNode; + currentSchedule.push_back(nextNode); + currentSum += w; + } + // check if this starting node is the best one + if (currentSum < optimalSum) + { + optimalSum = currentSum; + optimalSchedule.clear(); + optimalSchedule = currentSchedule; + } + } + return optimalSchedule; +} + +void +AquaSimTrumac::SendPacket() +{ + AquaSimHeader ash; + MacHeader mach; + // select next node_id + uint8_t next_node_id = selectNextNode(); + // std::cout << +next_node_id << "\n"; + + if (m_sendQueue.size() != 0) + { + // Get data packet from queue + Ptr data_packet = m_sendQueue.front(); + m_sendQueue.pop_front(); + + // Attach mac-headers to packet + data_packet->RemoveHeader(ash); + mach.SetSA(ash.GetSAddr()); + mach.SetDA(ash.GetDAddr()); + mach.SetDemuxPType(MacHeader::UWPTYPE_OTHER); + + TrumacHeader trmac_h; + trmac_h.SetPType(0); // 0 - data packet + trmac_h.SetNextNodeId(next_node_id); + + data_packet->AddHeader(trmac_h); + data_packet->AddHeader(mach); + data_packet->AddHeader(ash); + + m_lastTxStamp = Simulator::Now(); + + // send packet to PHY + SendDown(data_packet); + } + else + { + // Do not send empty token + // // prepare to send an empty packet (pass a token) + // Ptr empty_packet = Create(); + + // ash.SetDirection(AquaSimHeader::DOWN); + // ash.SetTxTime(MilliSeconds(8)); // TODO: define the token size + // ash.SetSize(10); // TODO: define the token size + + // mach.SetSA(AquaSimAddress::ConvertFrom(GetAddress())); + // mach.SetDA(AquaSimAddress::GetBroadcast()); + // mach.SetDemuxPType(MacHeader::UWPTYPE_OTHER); + + // TrumacHeader trmac_h; + // trmac_h.SetPType(1); // 1 - empty token + // trmac_h.SetNextNodeId(next_node_id); + + // empty_packet->AddHeader(trmac_h); + // empty_packet->AddHeader(mach); + // empty_packet->AddHeader(ash); + + // // send token to PHY + // SendDown(empty_packet); + } +} + +uint32_t +AquaSimTrumac::selectNextNode() +{ + uint32_t next_node = 0; + + if (m_algo_id == 0) + { + // if all the nodes have been overheard -> finish the TR-MAC cycle by selecting the starting node + if (m_heard_node_ids.size() == m_total_nodes - 1) + { + return m_start_node_id; + } + + // random selection + next_node = m_rand->GetInteger(0, m_total_nodes-1); + // while ((next_node == m_device->GetNode()->GetId()) || (next_node == m_start_node_id) || (std::find(m_heard_node_ids.begin(), m_heard_node_ids.end(), next_node) != m_heard_node_ids.end())) + while ((next_node == m_device->GetNode()->GetId()) || (std::find(m_heard_node_ids.begin(), m_heard_node_ids.end(), next_node) != m_heard_node_ids.end())) + { + // select another one + next_node = m_rand->GetInteger(0, m_total_nodes-1); + // std::cout << m_device->GetNode()->GetId() << " " << next_node << " " << m_heard_node_ids.size() << '\n'; + } + return next_node; + } + else if (m_algo_id == 1) + { + // sub-optimal TSP (greedy/nearest-neighbor selection TSP) + for (uint32_t i=0; iGetNode()->GetId()) + { + if (i != m_schedule.size()-1) + { + next_node = m_schedule.at(i+1); + } + else + { + // the last node in the schedule --> go to the first node + next_node = m_schedule.at(0); + } + break; + } + } + return next_node; + } + else + { + NS_FATAL_ERROR ("Unknown algorithm!"); + return 0; + } +} + +bool +AquaSimTrumac::RecvProcess (Ptr pkt) +{ + NS_LOG_FUNCTION(this); + + AquaSimHeader ash; + MacHeader mach; + TrumacHeader trmac_h; + pkt->RemoveHeader(ash); + pkt->RemoveHeader(mach); + pkt->RemoveHeader(trmac_h); + AquaSimAddress dst = mach.GetDA(); + AquaSimAddress src = mach.GetSA(); + + if ((Simulator::Now() - m_lastTxStamp) < m_contention_timeout) + { + // drop packet + } + + m_lastTxStamp = Simulator::Now(); + + if (ash.GetErrorFlag()) + { + NS_LOG_DEBUG("TrMac:RecvProcess: received corrupt packet."); + pkt=0; + return false; + } + + if (m_algo_id == 0) + { + // place the overheard node id to the list + uint8_t heard_id = getIdbyAddress(src); + if (heard_id == m_start_node_id) + { + m_heard_node_ids.clear(); + } + if (std::find(m_heard_node_ids.begin(), m_heard_node_ids.end(), heard_id) == m_heard_node_ids.end()) + { + m_heard_node_ids.push_back(heard_id); + } + // read the next sender id + // std::cout << "RECV NEXT: " << +trmac_h.GetNextNodeId() << " " << m_device->GetNode()->GetId() << "\n"; + if (trmac_h.GetNextNodeId() == m_device->GetNode()->GetId()) + { + if ((Simulator::Now() - m_lastRxTokenStamp) > Seconds(0.2)) + { + if (m_device->GetNode()->GetId() == m_start_node_id) + { + // initCycle(); + Simulator::Schedule(m_guard_time, &AquaSimTrumac::initCycle, this); + } + else + { + // if the sender id matches with the receiver id, then send a packet/token + // SendPacket(); + Simulator::Schedule(m_guard_time, &AquaSimTrumac::SendPacket, this); + } + m_lastRxTokenStamp = Simulator::Now(); + } + } + } + else if (m_algo_id == 1) + { + if (trmac_h.GetNextNodeId() == m_device->GetNode()->GetId()) + { + // if the sender id matches with the receiver id, then send a packet/token + // SendPacket(); + Simulator::Schedule(m_guard_time, &AquaSimTrumac::SendPacket, this); + } + } + + + if (AquaSimAddress::ConvertFrom(m_device->GetAddress()) == dst) + { + // trace the E2E delay + AquaSimTimeTag timeTag; + pkt->RemovePacketTag(timeTag); + m_e2eDelayTrace((Simulator::Now() - timeTag.GetTime()).GetMilliSeconds()); + + pkt->AddHeader(ash); + SendUp(pkt); + } + + pkt=0; + return false; +} + +uint32_t +AquaSimTrumac::getIdbyAddress(AquaSimAddress address) +{ + for (uint32_t i=0; i < m_device->GetChannel()->GetNDevices(); i++) + { + if (address == AquaSimAddress::ConvertFrom(m_device->GetChannel()->GetDevice(i)->GetAddress())) + { + return i; + } + } + NS_FATAL_ERROR ("No node with such address!"); + return 0; +} + +bool +AquaSimTrumac::TxProcess(Ptr pkt) +{ + NS_LOG_FUNCTION(this << pkt); + + // Attach a timestamp tag to calculate E2E delay + AquaSimTimeTag timeTag; + timeTag.SetTime(Simulator::Now()); + pkt->AddPacketTag(timeTag); + + // Put incoming data packets to send-queue + m_sendQueue.push_back(pkt); + m_queueSizeTrace(m_sendQueue.size()); + + return true; +} + +void AquaSimTrumac::DoDispose() +{ + NS_LOG_FUNCTION(this); + AquaSimMac::DoDispose(); +} diff --git a/model/aqua-sim-mac-trumac.h b/model/aqua-sim-mac-trumac.h new file mode 100644 index 0000000..c9eaa7c --- /dev/null +++ b/model/aqua-sim-mac-trumac.h @@ -0,0 +1,102 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2021 The City University of New York + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Dmitrii Dugaev + */ + +#ifndef AQUA_SIM_MAC_TRUMAC_H +#define AQUA_SIM_MAC_TRMUAC_H + +#include "aqua-sim-mac.h" +#include "aqua-sim-header.h" +#include "aqua-sim-header-mac.h" + +namespace ns3 { + +/** + * \ingroup aqua-sim-ng + * + * \brief Token-Ring MAC (TR-MAC) implementation + */ + +class AquaSimTrumac : public AquaSimMac +{ +public: + AquaSimTrumac(); + int64_t AssignStreams (int64_t stream); + + static TypeId GetTypeId(void); + + // Handle a packet coming from channel + virtual bool RecvProcess (Ptr); + // Handle a packet coming from upper layers + virtual bool TxProcess (Ptr); + + void initCycle(); + // check the sendQueue and send a packet, if the queue is not empty. Send an empty token otherwise. + void SendPacket(); + + // select next transmission node, based on the list of nodes already overheard and a total number of nodes in a swarm + uint32_t selectNextNode(); + + uint32_t getIdbyAddress(AquaSimAddress address); + + // run nearest-neightbor TSP algorithm, return sub-optimal sequence of nodes (schedule) + std::vector runNearestNeighborTSP(); + + // periodically initiate a contention-based data transmission, if no transmissions have been heard within a certain time interval + void initDataContention(); + +protected: + virtual void DoDispose(); + +private: + Ptr m_rand; + uint16_t m_packetSize; + std::list> m_sendQueue; + + // guard interval between packet reception and a packet transmission of the next packet + Time m_guard_time; + + // start contention-based transmission if no transmissions have happened within this timeout + Time m_contention_timeout; + + // starting node id + uint32_t m_start_node_id; + + // total number of nodes in a swarm + uint32_t m_total_nodes; + + uint32_t m_algo_id; + + // store list of all nodes' coordinates + std::map, double> m_graph; + std::vector m_schedule; + + // keep track of all the node ids, overheard during the TR-cycle + std::vector m_heard_node_ids; + + // keep track of the last overheard transmission + Time m_lastTxStamp = Seconds(0); + + Time m_lastRxTokenStamp = Seconds(0); + +}; // class AquaSimTrumac + +} // namespace ns3 + +#endif /* AQUA_SIM_MAC_TRUMAC_H */ diff --git a/model/aqua-sim-mac.cc b/model/aqua-sim-mac.cc index 78f4706..8c1c16c 100644 --- a/model/aqua-sim-mac.cc +++ b/model/aqua-sim-mac.cc @@ -95,6 +95,12 @@ AquaSimMac::GetTypeId(void) "Number of retransmitted data packets", MakeTraceSourceAccessor(&AquaSimMac::m_dataPktRetransmissions), "ns3::TracedValueCallback::Uint32") + .AddTraceSource ("QueueSizeTrace", + "Trace current send-queue size", + MakeTraceSourceAccessor (&AquaSimMac::m_queueSizeTrace), "ns3::Packet::TracedCallback") + .AddTraceSource ("E2EDelayTrace", + "Trace current end-to-end delay of a packet", + MakeTraceSourceAccessor (&AquaSimMac::m_e2eDelayTrace), "ns3::Packet::TracedCallback") ; return tid; } diff --git a/model/aqua-sim-mac.h b/model/aqua-sim-mac.h index 6c26a47..5a31fa6 100644 --- a/model/aqua-sim-mac.h +++ b/model/aqua-sim-mac.h @@ -114,6 +114,9 @@ class AquaSimMac : public Object { virtual int64_t AssignStreams (int64_t stream) = 0; + TracedCallback m_queueSizeTrace; + TracedCallback m_e2eDelayTrace; + private: // to receive packet from upper layer and lower layer //we hide this interface and demand derived classes to diff --git a/model/aqua-sim-phy-cmn.cc b/model/aqua-sim-phy-cmn.cc index 8319549..51c585e 100644 --- a/model/aqua-sim-phy-cmn.cc +++ b/model/aqua-sim-phy-cmn.cc @@ -153,6 +153,9 @@ AquaSimPhyCmn::GetTypeId(void) .AddTraceSource("Tx", "A packet was transmitted.", MakeTraceSourceAccessor (&AquaSimPhyCmn::m_txLogger), "ns3::AquaSimPhy::TracedCallback") + .AddTraceSource("RxColl", "Count collision on Rx", + MakeTraceSourceAccessor (&AquaSimPhyCmn::m_rxCollTrace), + "ns3::AquaSimPhy::TracedCallback") ; return tid; } @@ -477,6 +480,7 @@ AquaSimPhyCmn::PrevalidateIncomingPkt(Ptr p) asHeader.SetErrorFlag(true); // Set the collision flag to true as well m_collision_flag = true; + m_rxCollTrace(); } else { GetNetDevice()->SetTransmissionStatus(RECV); diff --git a/model/aqua-sim-phy-cmn.h b/model/aqua-sim-phy-cmn.h index 2d166d1..5b805c3 100644 --- a/model/aqua-sim-phy-cmn.h +++ b/model/aqua-sim-phy-cmn.h @@ -193,6 +193,7 @@ class AquaSimPhyCmn : public AquaSimPhy ns3::TracedCallback, double > m_rxLogger; ns3::TracedCallback, double > m_txLogger; + ns3::TracedCallback<> m_rxCollTrace; // Collision flag in order to monitor whether there have been incoming packets wihtin the TxTime delay of the original packet. // If yes, then mark the original packet as collided as well. diff --git a/model/aqua-sim-time-tag.cc b/model/aqua-sim-time-tag.cc new file mode 100644 index 0000000..022d3e1 --- /dev/null +++ b/model/aqua-sim-time-tag.cc @@ -0,0 +1,65 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + */ + +#include "aqua-sim-time-tag.h" + +namespace ns3 { + +TypeId +AquaSimTimeTag::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::AquaSimTimeTag") + .SetParent () + // .AddConstructor () + ; + return tid; +} + +TypeId +AquaSimTimeTag::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +uint32_t +AquaSimTimeTag::GetSerializedSize (void) const +{ + return 8; // uint64_t +} + +void +AquaSimTimeTag::Serialize (TagBuffer i) const +{ + i.WriteU64 (m_time); +} + +void +AquaSimTimeTag::Deserialize (TagBuffer i) +{ + m_time = i.ReadU64 (); +} + +void +AquaSimTimeTag::Print (std::ostream &os) const +{ + os << m_time; +} + +AquaSimTimeTag::AquaSimTimeTag () +{ +} + +void +AquaSimTimeTag::SetTime (Time time) +{ + m_time = time.GetNanoSeconds(); +} + +Time +AquaSimTimeTag::GetTime () +{ + return NanoSeconds(m_time); +} + +} // namespace ns3 diff --git a/model/aqua-sim-time-tag.h b/model/aqua-sim-time-tag.h new file mode 100644 index 0000000..a26b153 --- /dev/null +++ b/model/aqua-sim-time-tag.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + */ + +#ifndef AQUA_SIM_TIME_TAG +#define AQUA_SIM_TIME_TAG + +#include +#include "ns3/nstime.h" + +namespace ns3 { + +/** + * \ingroup Keep timestamp of the packet arrival from network layer + * + * Tag the packet with the arrival timestamp + * + */ +class AquaSimTimeTag : public Tag +{ +public: + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + TypeId GetInstanceTypeId (void) const; + + /** + * Constructor + */ + AquaSimTimeTag (); + + // Set chunk type + void SetTime (Time time); + // Return chunk type + Time GetTime (); + + // From class Tag + uint32_t GetSerializedSize (void) const; + void Serialize (TagBuffer i) const; + void Deserialize (TagBuffer i); + void Print (std::ostream &os) const; + + +private: + uint64_t m_time; // in nanoseconds +}; + +} // namespace ns3 + +#endif /* AQUA_SIM_TIME_TAG */ diff --git a/scripts/runJMAC.py b/scripts/runJMAC.py new file mode 100644 index 0000000..2062f9f --- /dev/null +++ b/scripts/runJMAC.py @@ -0,0 +1,41 @@ +#!/usr/bin/python +""" +Run JMAC on given number of nodes and iterations +""" + +import os, sys +import subprocess +import time + +# N_NODES = [6, 8, 10, 12, 14, 16, 18, 20, 22, 24] +N_NODES = [6] +N_ITERATIONS = 2 +MAC = "aloha" + +# NS-3 command line execute +path = "../../../" +cmd = './ns3 run "JmacTest --seed=%s --psize=400 --rate=120 --nodes=%s --mac=%s --sinks=1 --simStop=1000 --center_x=1000 --center_z=1000 --radius=300 --depth=400 --epochTime=10"' + +processes = [] + +def run(): + for n in N_NODES: + for i in range(N_ITERATIONS): + p = subprocess.Popen((path+cmd) % (i, n, MAC), shell=True) + print((path+cmd) % (i, n, MAC)) + processes.append(p) + time.sleep(2) + + p.wait() + + # # Wait till all processes are finished + # for p in processes: + # p.wait() + + # save results to a file + save_cmd = "mv ../../../jmac_results.txt ../../../results_%s_%s_%s.txt" % (MAC, n, N_ITERATIONS) + subprocess.Popen(save_cmd, shell=True) + time.sleep(1) + +if __name__ == '__main__': + run() diff --git a/scripts/runTRUMAC.py b/scripts/runTRUMAC.py new file mode 100644 index 0000000..87d3ea5 --- /dev/null +++ b/scripts/runTRUMAC.py @@ -0,0 +1,51 @@ +#!/usr/bin/python +""" +Run TRUMAC/TDMA/ALOHA on given number of nodes and iterations +""" + +import os, sys +import subprocess +import time + +# N_NODES = [6, 8, 10, 12, 14, 16, 18, 20, 22, 24] +# N_NODES = [2, 4, 8, 12, 16, 20, 24, 28, 32, 36] +N_NODES = [2, 4, 8] +# N_NODES = [20, 60, 100, 140, 180, 220, 260] +# DIST = [100, 200, 300, 500, 1000] +N_ITERATIONS = 10 +MAC = "trumac" # trumac, aloha, tdma +ALGO = 0 # for trumac only: 0 - random selection; 1 - nearest neighbor (greedy) +rate = 120 +dist = 100 # meters + +# NS-3 command line execute +path = "../../../" +cmd = './ns3 run "TrumacTest --seed=%s --psize=100 --nodes=%s --simStop=1000 --mac=%s --rate=%s --dist=%s --algo=%s"' + +processes = [] + +def run(): + for n in N_NODES: + # for d in DIST: + for i in range(N_ITERATIONS): + # p = subprocess.Popen((path+cmd) % (i, n, MAC, rate), shell=True) + p = subprocess.Popen((path+cmd) % (i, n, MAC, rate, dist, ALGO), shell=True) + # print (path+cmd) % (i, 2, MAC, rate, d) + processes.append(p) + + time.sleep(0.2) + # p.wait() + + # Wait till all processes are finished + for p in processes: + p.wait() + time.sleep(0.2) + + # save results to a file + # save_cmd = "mv ../../../results_trumac.txt ../../../results_trumac_%s_%s_%s_%s.txt" % (MAC, rate, n, N_ITERATIONS) + save_cmd = "mv ../../../results_trumac.txt ../../../results_%s_%s_%s.txt" % (MAC, n, ALGO) + subprocess.Popen(save_cmd, shell=True) + time.sleep(1) + +if __name__ == '__main__': + run()