In the third experiment, we will benefit from the power of Python as a configuration language. The main goal of this experiment is to setup the scenario shown in the following figure: One AP is situated in the center, all STAs surround the AP on a circle with a variable radius r. The number of STAs n can be changed, but the positioning of the STAs on the circle shall always be equidistant.
Scenario of experiment 3
To support a flexible scenario setup, we will use two important features of Python:
Using ./playground.py preparecampaign PATH with the same PATH as in the preparation for experiment 1 (namely ../myFirstCampaign if you did follow the chapter “Preparations” before Experiment 1), we can create a sub-campaign that uses the same sandbox, but a differed directory to store the simulations. When prompted by ./playground.py, please select (C)reate a sub-campaign and choose as name e.g. Experiment3.
As the scenario has similarities to the one created in the first two experiments, we can re-use the config.py and the campaignConfiguration.py by copying them to the new directory Experiment3. The following description will use those two files.
The config.py file from the first experiments required several lines of code to generate and configure the WiFiMAC-part of the STA:
# Create Station Transceiver
staConfig = wifimac.support.Transceiver.Station(frequency = networkFrequency,
position = openwns.geometry.position.Position(distance, 0, 0),
scanFrequencies = [networkFrequency],
scanDuration = 0.3)
# Transmission power
staConfig.txPower = dBm(20)
# rate adaptation strategy: ARF based
staConfig.layer2.ra.raStrategy = ARF()
# For frames above this threshold (in bit) RTS/CTS will be used
staConfig.layer2.rtsctsThreshold = 8e6
# Create station node
sta = nc.createSTA(idGen, managerPool, rang,
config = staConfig,
loggerLevel = commonLoggerLevel,
dllLoggerLevel = dllLoggerLevel)
As all n STAs will have the same configuration, we now would like to replace those lines with the following code:
staConfig = MySTATransceiver(position = openwns.geometry.position.Position(x, y, 0))
sta = nc.createSTA(idGen, managerPool, rang, config = staConfig,
loggerLevel = commonLoggerLevel,
dllLoggerLevel = dllLoggerLevel)
Therefore, a class MySTATransceiver is needed, which is derived from the basic STA-Transceiver class wifimac.support.Transceiver.Station. This is done in the following way (before the creation of the STAs):
###########################
# Transceiver configuration
# configuration class for STAs
class MySTATransceiver(wifimac.support.Transceiver.Station):
def __init__(self, position):
super(MySTATransceiver, self).__init__(frequency = networkFrequency,
position = position,
scanFrequencies = [networkFrequency],
scanDuration = 0.3)
# Transmission power
self.txPower = dBm(20)
# rate adaptation strategy: None, constant BPSK 1/2
self.layer2.ra.raStrategy = Constant()
# For frames above this threshold (in bit) RTS/CTS will be used
self.layer2.rtsctsThreshold = 8e6
# End node configuration
########################
This class inherits the default configuration; this default values can the be changed. Additionally, the creator function __init__ can be changed to the meet requirements of the scenario. In detail, the new specialised class works in the following way:
With this new class, STAs can be created using the code above. Of course, the traffic generation has to be set for every station, similar to experiment 1.
Like any other programming language (but unlike static configuration files), Python provides a for - loop construct which we will use to generate as many STAs as required. Based on the STA number, we will calculate the x/y position so that the STAs are placed on the circle with equidistant separation. Thus, the loop looks like
# Create STAs in circle around AP
from math import sin, cos, pi
for s in xrange(numSTAs):
x = radius + radius * cos(float(s)/numSTAs*2*pi)
y = radius + radius * sin(float(s)/numSTAs*2*pi)
Of course, the remaining creation of the STA (including the traffic and the addition to WNS.nodes must be inside this loop.
Note
The resulting config.py with the described changes is also available in the directory myOpenWNS/tests/system/wifimac-tests/PyConfig/experiment3
Where is approximately the saturation point?
As in experiment 2, we can again use the binary search to find the saturation point without manual trial-and-error simulations. To do this, we have to adapt the function getTotalThroughput, as now the offered traffic is given per STA, but the measured throughput (e.g. by the probe ip.endToEnd.window.incoming.bitThroughput_Moments) in the RANG is the combined throughput of all STAs. Hence, for a retrieval of f(input) = output from the database, we have to
This is done by the following getTotalThroughputPerSTA function, which can be found in the campaignConfiguration.py prepared in the directory myOpenWNS/tests/system/wifimac-tests/PyConfig/experiment3:
def getTotalThroughputPerSTA(paramsString, inputName, cursor):
myQuery = " \
SELECT idResults.scenario_id, idResults." + inputName + ", idResults.numstations, VAL.mean \
FROM moments VAL, (SELECT scenario_id, " + inputName + ", numstations FROM parameter_sets WHERE " + paramsString + ") AS idResults \
WHERE VAL.scenario_id = idResults.scenario_id AND \
VAL.alt_name = 'ip.endToEnd.window.incoming.bitThroughput_Moments' \
ORDER BY idResults." + inputName + ";"
cursor.execute(myQuery)
resultsIn = cursor.fetchall()
myQuery = " \
SELECT idResults.scenario_id, idResults." + inputName + ", idResults.numstations, VAL.mean \
FROM moments VAL, (SELECT scenario_id, " + inputName + ", numstations FROM parameter_sets WHERE " + paramsString + ") AS idResults \
WHERE VAL.scenario_id = idResults.scenario_id AND \
VAL.alt_name = 'ip.endToEnd.window.aggregated.bitThroughput_Moments' \
ORDER BY idResults." + inputName + ";"
cursor.execute(myQuery)
resultsAgg = cursor.fetchall()
results = []
for i in zip(resultsAgg, resultsIn):
agg = i[0]
inc = i[1]
assert(agg[0] == inc[0])
assert(agg[1] == inc[1])
assert(agg[2] == inc[2])
results.append([agg[0], agg[1], (agg[3] + inc[3])/agg[2]])
return results
As we can see, the two queries (for incoming and aggregated traffic) result a 4-column list, where the 3rd column contains the number of stations.
Besides this change (and the new simulation parameters), the file is the same as in experiment 2.