以wifi-example-sim.cc為例說明NS3統計資料模型
利用NS3已有的Trace系統或者Log機制收集記錄和統計資料,例如MAC層收發幀數目,網路層以上收發包數目的跟蹤與統計,這裡選取example/stats/wifi-example-sim.cc為例來很好說明問題:
這個模擬程式是一個簡單的實驗,包括兩個節點,基於AdhocMAC通道模型,包含NS3模擬所需常見模型如節點/網路裝置/協議棧和應用程序,這裡的應用程序Sender 和Receiver,基於UDP的不可靠連線。
一 給定本次模擬引數distance,format,experiment,strategy,runID在初始化的同時也可以通過命令列改變,這些引數用於從多次實驗中快速區分和組合資料。#include <ctime> #include <sstream> #include "ns3/core-module.h" #include "ns3/network-module.h" #include "ns3/mobility-module.h" #include "ns3/wifi-module.h" #include "ns3/internet-module.h" #include "ns3/stats-module.h" #include "wifi-example-apps.h" using namespace ns3; using namespace std; NS_LOG_COMPONENT_DEFINE ("WiFiDistanceExperiment"); void TxCallback (Ptr<CounterCalculator<uint32_t> > datac, std::string path, Ptr<const Packet> packet) { NS_LOG_INFO ("Sent frame counted in " << datac->GetKey ()); datac->Update (); // end TxCallback } //---------------------------------------------------------------------- //-- main //---------------------------------------------- int main (int argc, char *argv[]) { double distance = 50.0; string format ("omnet"); string experiment ("wifi-distance-test"); string strategy ("wifi-default"); string input; string runID; { stringstream sstr; sstr << "run-" << time (NULL); runID = sstr.str (); } // Set up command line parameters used to control the experiment. CommandLine cmd; cmd.AddValue ("distance", "Distance apart to place nodes (in meters).", distance); cmd.AddValue ("format", "Format to use for data output.", format); cmd.AddValue ("experiment", "Identifier for experiment.", experiment); cmd.AddValue ("strategy", "Identifier for strategy.", strategy); cmd.AddValue ("run", "Identifier for run.", runID); cmd.Parse (argc, argv); if (format != "omnet" && format != "db") { NS_LOG_ERROR ("Unknown output format '" << format << "'"); return -1; } #ifndef STATS_HAS_SQLITE3 if (format == "db") { NS_LOG_ERROR ("sqlite support not compiled in."); return -1; } #endif { stringstream sstr (""); sstr << distance; input = sstr.str (); } //------------------------------------------------------------ //-- Create nodes and network stacks //-------------------------------------------- NS_LOG_INFO ("Creating nodes."); NodeContainer nodes; nodes.Create (2); NS_LOG_INFO ("Installing WiFi and Internet stack."); WifiHelper wifi; WifiMacHelper wifiMac; wifiMac.SetType ("ns3::AdhocWifiMac"); YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default (); YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default (); wifiPhy.SetChannel (wifiChannel.Create ()); NetDeviceContainer nodeDevices = wifi.Install (wifiPhy, wifiMac, nodes); InternetStackHelper internet; internet.Install (nodes); Ipv4AddressHelper ipAddrs; ipAddrs.SetBase ("192.168.0.0", "255.255.255.0"); ipAddrs.Assign (nodeDevices); //------------------------------------------------------------ //-- Setup physical layout //-------------------------------------------- NS_LOG_INFO ("Installing static mobility; distance " << distance << " ."); MobilityHelper mobility; Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>(); positionAlloc->Add (Vector (0.0, 0.0, 0.0)); positionAlloc->Add (Vector (0.0, distance, 0.0)); mobility.SetPositionAllocator (positionAlloc); mobility.Install (nodes); //------------------------------------------------------------ //-- Create a custom traffic source and sink //-------------------------------------------- NS_LOG_INFO ("Create traffic source & sink."); Ptr<Node> appSource = NodeList::GetNode (0); Ptr<Sender> sender = CreateObject<Sender>(); appSource->AddApplication (sender); sender->SetStartTime (Seconds (1)); Ptr<Node> appSink = NodeList::GetNode (1); Ptr<Receiver> receiver = CreateObject<Receiver>(); appSink->AddApplication (receiver); receiver->SetStartTime (Seconds (0)); Config::Set ("/NodeList/*/ApplicationList/*/$Sender/Destination", Ipv4AddressValue ("192.168.0.2")); //------------------------------------------------------------ //-- Setup stats and data collection //-------------------------------------------- // Create a DataCollector object to hold information about this run. DataCollector data; data.DescribeRun (experiment,//experiment 是物件 strategy,//strategy 是被檢查的程式碼或者引數 input,//2個節點距離 runID); // Add any information we wish to record about this run. data.AddMetadata ("author", "tjkopena"); // Create a counter to track how many frames are generated. Updates // are triggered by the trace signal generated by the WiFi MAC model // object. Here we connect the counter to the signal via the simple // TxCallback() glue function defined above. Ptr<CounterCalculator<uint32_t> > totalTx = CreateObject<CounterCalculator<uint32_t> >(); totalTx->SetKey ("wifi-tx-frames"); totalTx->SetContext ("node[0]"); Config::Connect ("/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Mac/MacTx", MakeBoundCallback (&TxCallback, totalTx)); data.AddDataCalculator (totalTx); // This is similar, but creates a counter to track how many frames // are received. Instead of our own glue function, this uses a // method of an adapter class to connect a counter directly to the // trace signal generated by the WiFi MAC. Ptr<PacketCounterCalculator> totalRx = CreateObject<PacketCounterCalculator>(); totalRx->SetKey ("wifi-rx-frames"); totalRx->SetContext ("node[1]"); Config::Connect ("/NodeList/1/DeviceList/*/$ns3::WifiNetDevice/Mac/MacRx", MakeCallback (&PacketCounterCalculator::PacketUpdate, totalRx)); data.AddDataCalculator (totalRx); // This counter tracks how many packets---as opposed to frames---are // generated. This is connected directly to a trace signal provided // by our Sender class. Ptr<PacketCounterCalculator> appTx = CreateObject<PacketCounterCalculator>(); appTx->SetKey ("sender-tx-packets"); appTx->SetContext ("node[0]"); Config::Connect ("/NodeList/0/ApplicationList/*/$Sender/Tx", MakeCallback (&PacketCounterCalculator::PacketUpdate, appTx)); data.AddDataCalculator (appTx); // Here a counter for received packets is directly manipulated by // one of the custom objects in our simulation, the Receiver // Application. The Receiver object is given a pointer to the // counter and calls its Update() method whenever a packet arrives. Ptr<CounterCalculator<> > appRx = CreateObject<CounterCalculator<> >(); appRx->SetKey ("receiver-rx-packets"); appRx->SetContext ("node[1]"); receiver->SetCounter (appRx); data.AddDataCalculator (appRx); /** * Just to show this is here... Ptr<MinMaxAvgTotalCalculator<uint32_t> > test = CreateObject<MinMaxAvgTotalCalculator<uint32_t> >(); test->SetKey("test-dc"); data.AddDataCalculator(test); test->Update(4); test->Update(8); test->Update(24); test->Update(12); **/ // This DataCalculator connects directly to the transmit trace // provided by our Sender Application. It records some basic // statistics about the sizes of the packets received (min, max, // avg, total # bytes), although in this scenaro they're fixed. Ptr<PacketSizeMinMaxAvgTotalCalculator> appTxPkts = CreateObject<PacketSizeMinMaxAvgTotalCalculator>(); appTxPkts->SetKey ("tx-pkt-size"); appTxPkts->SetContext ("node[0]"); Config::Connect ("/NodeList/0/ApplicationList/*/$Sender/Tx", MakeCallback (&PacketSizeMinMaxAvgTotalCalculator::PacketUpdate, appTxPkts)); data.AddDataCalculator (appTxPkts); // Here we directly manipulate another DataCollector tracking min, // max, total, and average propagation delays. Check out the Sender // and Receiver classes to see how packets are tagged with // timestamps to do this. Ptr<TimeMinMaxAvgTotalCalculator> delayStat = CreateObject<TimeMinMaxAvgTotalCalculator>(); delayStat->SetKey ("delay"); delayStat->SetContext ("."); receiver->SetDelayTracker (delayStat); data.AddDataCalculator (delayStat); //------------------------------------------------------------ //-- Run the simulation //-------------------------------------------- NS_LOG_INFO ("Run Simulation."); Simulator::Run (); //------------------------------------------------------------ //-- Generate statistics output. //-------------------------------------------- // Pick an output writer based in the requested format. Ptr<DataOutputInterface> output = 0; if (format == "omnet") { NS_LOG_INFO ("Creating omnet formatted data output."); output = CreateObject<OmnetDataOutput>(); } else if (format == "db") { #ifdef STATS_HAS_SQLITE3 NS_LOG_INFO ("Creating sqlite formatted data output."); output = CreateObject<SqliteDataOutput>(); #endif } else { NS_LOG_ERROR ("Unknown output format " << format); } // Finally, have that writer interrogate the DataCollector and save // the results. if (output != 0) output->Output (data); // Free any memory here at the end of this example. Simulator::Destroy (); // end main }
二 建立節點和網路模型
三 安裝協議棧,並分配IP
四 設定移動模型,這裡為靜止,並給定初始位置
五 安裝應用,這裡安裝Sender / Receiver,自定義的見examples/stats/wifi-example-apps.h|cc
六 資料統計與收集,這是本文重點,下面具體分析。
這裡建立DataCollector物件來儲存執行資訊,並通過Trace機制記錄收發端幀和分組傳輸情況。
1 記錄發端幀傳輸(基WIFI MAC對界)
通過CounterCalculator(src/stats/model/basic-data-calculators.h )類實現計數,利用Trace機制,當節點0上wifiNetDevice/Mac/MacTx變化(source),通過Config::Connect關聯,定義的TxCallback作為sink函式呼叫,導致CounterCalculator::update呼叫即m_count++從而起到計數功能;
2 記錄收端幀傳輸(基WIFI MAC對界)
類似情況1,雖然這裡的sink函式是PacketConterCalculator::PacketUpdate(src/network/utils/packet-data-calculators.cc
3 記錄發端分組傳輸
也是通過PacketConterCalculator::PacketUpdate實現計數,利用Trace機制,當節點0上/Application/*/$Sender/Tx變化(source),通過通過Config::Connect關聯,定義的PacketConterCalculator::PacketUpdate作為sink函式呼叫;
4 記錄收端分組接收
由於收端應用Receiver沒有定義traced source,故這裡沒有采用Trace機制,而是直接利用Receiver:;SetCounter直接操作,通過SetCounter顯示型別轉換,j將appRx賦值給Receiver內部計數器,從而實現計數
以上均是通過PacketConterCalculator(src/network/utils/packet-data-calculators.cc)或者CounterCalculator(src/stats/model/basic-data-calculators.h )實現傳輸單元的計數,\下一個將通過引入PacketSizeMinMaxAvgTotalCalculator (src/network/utils/packet-data-calculators.h|cc)和MinMaxAvgTotalCalculator(src/stats/model/basic-data-calculators.h)實現單元內大小的記錄。
5 記錄發端分組大小
這裡採用Trace機制,節點0上/Application/*/$Sender/Tx變化(source),通過通過Config::Connect關聯,定義的PacketSizeMinMaxAvgTotalCalculator::PacketUpdate作為sink函式呼叫,從而MinMaxAvgTotalCalculator::Update實現大小的記錄。
6 記錄端到端產生分組時的延遲
類似情況4,不採用Trace機制,直接利用Receiver:;SetDelayTracker記錄傳世時延最值/平均值等
七 執行程式命令
八 統計結果輸出
對於輸出要麼OMNet++(純文字輸出格式)要麼SQLite(資料庫格式輸出),這取決於程式頭部定義的引數format,並最終DataCollector物件進行儲存。
九 控制指令碼實現最後執行
通過 一個簡單的控制指令碼實現該模擬程式在不同距離下大量重複(作為輸入)實驗後執行畫圖。可參考example/stats/wifi-example-db.sh(以後自己寫多個不同輸入下重複模擬專案時可參考這個)。該執行指令碼每次都是基於一個不同的距離作為輸入,收集每次模擬結果到SQLite資料庫,其中對於每個距離輸入,進行5次重複實驗以減小波動。全部模擬完成只需幾十秒,在完成儲存到資料庫後,可通過SQLite命令列進行SQL查詢。並呼叫 wifi-example.gnuplot畫圖
進入該目錄
cd /NS3/ns-allinone-3.25/ns-3.25/examples/stats
./wifi-example-db.sh
產生data.db資料庫,wifi-default.data和wifi-default.eps圖
圖是對應距離下的丟包率以表徵WiFi模型效能。