1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2017 Orange Labs
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  * Author: Rediet <getachew.redieteab@orange.com>
19  */
20 
21 #include "ns3/gnuplot.h"
22 #include "ns3/command-line.h"
23 #include "ns3/string.h"
24 #include "ns3/ssid.h"
25 #include "ns3/spectrum-helper.h"
26 #include "ns3/spectrum-wifi-helper.h"
27 #include "ns3/spectrum-analyzer-helper.h"
28 #include "ns3/spectrum-channel.h"
29 #include "ns3/mobility-helper.h"
30 
31 using namespace ns3;
32 
33 /**
34  * This example (inspired from tv-trans-example) enables to generate the transmitted spectra of
35  * Wi-Fi stations, so as to model transmit mask imperfections of OFDM-based Wi-Fi standards.
36  * Only one data packet is sent from access point to station (once association has been performed)
37  * so as to reduce execution time.
38  *
39  * A spectrum analyzer is used to measure the transmitted spectra from Wi-Fi stations.
40  * The file "spectrum-analyzer-wifi-[standard]-[bandwidth]MHz-sim-2-0.tr" contains its
41  * output post simulation and use it to plot transmitted spectra with Gnuplot.
42  *
43  * The wifi-trans-example.sh script runs this example for all combinations, plots transmitted spectra,
44  * and puts resulting png images in wifi-trans-results folder.
45  */
46 
47 void
SendPacket(Ptr<NetDevice> sourceDevice,Address & destination)48 SendPacket (Ptr<NetDevice> sourceDevice, Address& destination)
49 {
50   Ptr<Packet> pkt = Create<Packet> (100);  // dummy bytes of data
51   sourceDevice->Send (pkt, destination, 0);
52 }
53 
main(int argc,char ** argv)54 int main (int argc, char** argv)
55 {
56   std::string standard = "11a";
57   int bw = 20;
58   double pow = 23; //dBm
59   bool verbose = false;
60   CommandLine cmd (__FILE__);
61   cmd.AddValue ("standard",
62                 "OFDM-based Wi-Fi standard [11a, 11p_10MHZ, 11p_5MHZ, 11n_2_4GHZ, 11n_5GHZ, 11ac, 11ax_2_4GHZ, 11ax_5GHZ]",
63                 standard);
64   cmd.AddValue ("bw", "Bandwidth (consistent with standard, in MHz)", bw);
65   cmd.AddValue ("txPower", "Transmit power (dBm)", pow);
66   cmd.AddValue ("verbose", "Display log messages for WifiSpectrumValueHelper and SpectrumWifiPhy", verbose);
67   cmd.Parse (argc,argv);
68 
69   WifiHelper wifi;
70   Ssid ssid;
71   std::string dataRate;
72   int freq;
73   Time dataStartTime = MicroSeconds (800); // leaving enough time for beacon and association procedure
74   Time dataDuration = MicroSeconds (300); // leaving enough time for data transfer (+ acknowledgment)
75   if (standard == "11a")
76     {
77       wifi.SetStandard (WIFI_STANDARD_80211a);
78       ssid = Ssid ("ns380211a");
79       dataRate = "OfdmRate6Mbps";
80       freq = 5180;
81       if (bw != 20)
82         {
83           std::cout << "Bandwidth is not compatible with standard" << std::endl;
84           return 1;
85         }
86     }
87   else if (standard == "11p_10MHZ")
88     {
89       wifi.SetStandard (WIFI_STANDARD_80211p);
90       ssid = Ssid ("ns380211p_10MHZ");
91       dataRate = "OfdmRate3MbpsBW10MHz";
92       freq = 5860;
93       dataStartTime = MicroSeconds (1400);
94       dataDuration = MicroSeconds (600);
95       if (bw != 10)
96         {
97           std::cout << "Bandwidth is not compatible with standard" << std::endl;
98           return 1;
99         }
100     }
101   else if (standard == "11p_5MHZ")
102     {
103       wifi.SetStandard (WIFI_STANDARD_80211p);
104       ssid = Ssid ("ns380211p_5MHZ");
105       dataRate = "OfdmRate1_5MbpsBW5MHz";
106       freq = 5860;
107       dataStartTime = MicroSeconds (2500);
108       dataDuration = MicroSeconds (1200);
109       if (bw != 5)
110         {
111           std::cout << "Bandwidth is not compatible with standard" << std::endl;
112           return 1;
113         }
114     }
115   else if (standard == "11n_2_4GHZ")
116     {
117       wifi.SetStandard (WIFI_STANDARD_80211n_2_4GHZ);
118       ssid = Ssid ("ns380211n_2_4GHZ");
119       dataRate = "HtMcs0";
120       freq = 2402 + (bw / 2); //so as to have 2412/2422 for 20/40
121       dataStartTime = MicroSeconds (4700);
122       dataDuration = MicroSeconds (400);
123       if (bw != 20 && bw != 40)
124         {
125           std::cout << "Bandwidth is not compatible with standard" << std::endl;
126           return 1;
127         }
128     }
129   else if (standard == "11n_5GHZ")
130     {
131       wifi.SetStandard (WIFI_STANDARD_80211n_5GHZ);
132       ssid = Ssid ("ns380211n_5GHZ");
133       dataRate = "HtMcs0";
134       freq = 5170 + (bw / 2); //so as to have 5180/5190 for 20/40
135       dataStartTime = MicroSeconds (1000);
136       if (bw != 20 && bw != 40)
137         {
138           std::cout << "Bandwidth is not compatible with standard" << std::endl;
139           return 1;
140         }
141     }
142   else if (standard == "11ac")
143     {
144       wifi.SetStandard (WIFI_STANDARD_80211ac);
145       ssid = Ssid ("ns380211ac");
146       dataRate = "VhtMcs0";
147       freq = 5170 + (bw / 2); //so as to have 5180/5190/5210/5250 for 20/40/80/160
148       dataStartTime = MicroSeconds (1100);
149       dataDuration += MicroSeconds (400); //account for ADDBA procedure
150       if (bw != 20 && bw != 40 && bw != 80 && bw != 160)
151         {
152           std::cout << "Bandwidth is not compatible with standard" << std::endl;
153           return 1;
154         }
155     }
156   else if (standard == "11ax_2_4GHZ")
157     {
158       wifi.SetStandard (WIFI_STANDARD_80211ax_2_4GHZ);
159       ssid = Ssid ("ns380211ax_2_4GHZ");
160       dataRate = "HeMcs0";
161       freq = 2402 + (bw / 2); //so as to have 2412/2422/2442 for 20/40/80
162       dataStartTime = MicroSeconds (5500);
163       dataDuration += MicroSeconds (2000); //account for ADDBA procedure
164       if (bw != 20 && bw != 40 && bw != 80)
165         {
166           std::cout << "Bandwidth is not compatible with standard" << std::endl;
167           return 1;
168         }
169     }
170   else if (standard == "11ax_5GHZ")
171     {
172       wifi.SetStandard (WIFI_STANDARD_80211ax_5GHZ);
173       ssid = Ssid ("ns380211ax_5GHZ");
174       dataRate = "HeMcs0";
175       freq = 5170 + (bw / 2); //so as to have 5180/5190/5210/5250 for 20/40/80/160
176       dataStartTime = MicroSeconds (1200);
177       dataDuration += MicroSeconds (500); //account for ADDBA procedure
178       if (bw != 20 && bw != 40 && bw != 80 && bw != 160)
179         {
180           std::cout << "Bandwidth is not compatible with standard" << std::endl;
181           return 1;
182         }
183     }
184   else
185     {
186       std::cout << "Unknown OFDM standard (please refer to the listed possible values)" << std::endl;
187       return 1;
188     }
189 
190   if (verbose)
191     {
192       LogComponentEnableAll (LOG_PREFIX_ALL);
193       LogComponentEnable ("WifiSpectrumValueHelper", LOG_LEVEL_ALL);
194       LogComponentEnable ("SpectrumWifiPhy", LOG_LEVEL_ALL);
195     }
196 
197   /* nodes and positions */
198   NodeContainer wifiNodes;
199   NodeContainer spectrumAnalyzerNodes;
200   NodeContainer allNodes;
201   wifiNodes.Create (2);
202   spectrumAnalyzerNodes.Create (1);
203   allNodes.Add (wifiNodes);
204   allNodes.Add (spectrumAnalyzerNodes);
205   NodeContainer wifiStaNode;
206   NodeContainer wifiApNode;
207   wifiApNode.Add (wifiNodes.Get (0));
208   wifiStaNode.Add (wifiNodes.Get (1));
209 
210   /* channel and propagation */
211   SpectrumChannelHelper channelHelper = SpectrumChannelHelper::Default ();
212   channelHelper.SetChannel ("ns3::MultiModelSpectrumChannel");
213   // constant path loss added just to show capability to set different propagation loss models
214   // FriisSpectrumPropagationLossModel already added by default in SpectrumChannelHelper
215   channelHelper.AddSpectrumPropagationLoss ("ns3::ConstantSpectrumPropagationLossModel");
216   Ptr<SpectrumChannel> channel = channelHelper.Create ();
217 
218   /* Wi-Fi transmitter setup */
219 
220   SpectrumWifiPhyHelper spectrumPhy;
221   spectrumPhy.SetChannel (channel);
222   spectrumPhy.SetErrorRateModel ("ns3::NistErrorRateModel");
223   spectrumPhy.Set ("Frequency", UintegerValue (freq));
224   spectrumPhy.Set ("ChannelWidth", UintegerValue (bw));
225   spectrumPhy.Set ("TxPowerStart", DoubleValue (pow)); // dBm
226   spectrumPhy.Set ("TxPowerEnd", DoubleValue (pow));
227 
228   WifiMacHelper mac;
229   wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager","DataMode", StringValue (dataRate),
230                                 "ControlMode", StringValue (dataRate));
231 
232   mac.SetType ("ns3::StaWifiMac",
233                "Ssid", SsidValue (ssid),
234                "ActiveProbing", BooleanValue (false));
235   NetDeviceContainer staDevice = wifi.Install (spectrumPhy, mac, wifiStaNode);
236   mac.SetType ("ns3::ApWifiMac",
237                "Ssid", SsidValue (ssid),
238                "EnableBeaconJitter", BooleanValue (false)); // so as to be sure that first beacon arrives quickly
239   NetDeviceContainer apDevice = wifi.Install (spectrumPhy, mac, wifiApNode);
240 
241   MobilityHelper mobility;
242   Ptr<ListPositionAllocator> nodePositionList = CreateObject<ListPositionAllocator> ();
243   nodePositionList->Add (Vector (0.0, 1.0, 0.0)); // AP
244   nodePositionList->Add (Vector (1.0, 0.0, 0.0)); // STA
245   nodePositionList->Add (Vector (0.0, 0.0, 0.0));  // Spectrum Analyzer
246   mobility.SetPositionAllocator (nodePositionList);
247   mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
248   mobility.Install (allNodes);
249 
250   /* Need to send data packet because beacon and association frames shall be sent using lowest rate */
251   // Send one data packet (this packet is sent using data rate / MCS defined above) once association is done (otherwise dropped)
252   Simulator::Schedule (dataStartTime, &SendPacket, apDevice.Get (0), staDevice.Get (0)->GetAddress ());
253 
254   /* frequency range for spectrum analyzer */
255   std::vector<double> freqs;
256   int margin = 2; //1MHz margin on each side
257   int band = (bw + margin);
258   for (int i = 0; i < (4 * 10 * band); ++i) //conversion to 100kHz scale
259     {
260       freqs.push_back (i * 1e5 + (freq - 2 * band) * 1e6);
261     }
262   Ptr<SpectrumModel> spectrumAnalyzerFreqModel = Create<SpectrumModel> (freqs);
263 
264   /* spectrum analyzer setup */
265   SpectrumAnalyzerHelper spectrumAnalyzerHelper;
266   spectrumAnalyzerHelper.SetChannel (channel);
267   spectrumAnalyzerHelper.SetRxSpectrumModel (spectrumAnalyzerFreqModel);
268   spectrumAnalyzerHelper.SetPhyAttribute ("Resolution", TimeValue (MicroSeconds (4))); //enough resolution to distinguish OFDM symbols (default 1ms too long even for PPDUs)
269   std::ostringstream ossFileName;
270   ossFileName << "spectrum-analyzer-wifi-" << standard << "-" << bw << "MHz";
271   spectrumAnalyzerHelper.EnableAsciiAll (ossFileName.str ());
272   NetDeviceContainer spectrumAnalyzerDevices = spectrumAnalyzerHelper.Install (spectrumAnalyzerNodes);
273 
274   /* Let enough time for first beacon, association procedure, and first data (+acknowledgment and eventually preceding ADDBA procedure) */
275   Simulator::Stop (dataStartTime + dataDuration);
276 
277   Simulator::Run ();
278 
279   /* Plot transmitted spectra with Gnuplot */
280   ossFileName << "-2-0"; // append node-interface info
281   std::ostringstream ossPlt;
282   ossPlt << ossFileName.str () << ".plt";
283   std::ofstream plotFile (ossPlt.str ());
284   std::ostringstream ossPng;
285   ossPng << ossFileName.str () << ".png";
286   Gnuplot plot = Gnuplot (ossPng.str ());
287   //Prepare 3D plot (reset previous values)
288   std::ostringstream ossExtra;
289   ossExtra << "file = '" << ossFileName.str () << "'";
290   plot.SetExtra (ossExtra.str ());
291   plot.AppendExtra ("unset surface");
292   plot.AppendExtra ("set key off");
293   //Configure output file as png
294   plot.AppendExtra ("set term png");
295   plot.AppendExtra ("set output file . '.png'");
296   //Switch to 3D plot
297   plot.AppendExtra ("set pm3d at s");
298   plot.AppendExtra ("set palette");
299   //Orient view
300   plot.AppendExtra ("set view 50,50");
301   //Add legends
302   plot.AppendExtra ("set xlabel \"time (ms)\"");
303   plot.AppendExtra ("set ylabel \"freq (MHz)\" offset 15,0,0");
304   plot.AppendExtra ("set zlabel \"PSD (dBW/Hz)\" offset 15,0,0");
305   //Define grid
306   plot.AppendExtra ("set ytics");
307   plot.AppendExtra ("set mytics 2");
308   plot.AppendExtra ("set ztics");
309   plot.AppendExtra ("set mztics 5");
310   plot.AppendExtra ("set grid ytics mytics ztics mztics");
311   //tr file name
312   plot.AppendExtra ("filename = file . '.tr'");
313   //Extract max power using stats (so as to normalize during display)
314   plot.AppendExtra ("stats filename using 3");
315   plot.AppendExtra ("refW = STATS_max");
316   //Plot graph (file being defined upon gnuplot call)
317   plot.AppendExtra ("splot filename using ($1*1000.0):($2/1e6):(10*log10($3/refW))");
318   //Generate output and close file
319   plot.GenerateOutput (plotFile);
320   plotFile.close ();
321 
322   Simulator::Destroy ();
323 
324   std::cout << "Simulation done!" << std::endl;
325   std::cout << "See spectrum analyzer output file: " << ossFileName.str () << ".tr" << std::endl;
326   std::cout << "To generate plot simply execute the following command: gnuplot " << ossFileName.str () << ".plt" << std::endl;
327 
328   return 0;
329 }
330