This notebook illustrates the simulation of an Alamouti MIMO scheme transmission through a flat fading rayleight channel.
We simulate the Alamouti STBC with a 16-QAM modulation, as well as BPSK modulation with 1 and two receive antennas.
Note: This notebook uses the parallel capabilities of IPython. Thus you need to start the engines in the cluster pannel before running the code here.
First we create a IPython parallel client so that we can communicate with the engines.
In [44]:
%matplotlib inline
from matplotlib import pyplot as plt
In [1]:
from IPython.parallel import Client
cl = Client()
cl.clear() # Erases the workspace of the engines.
bview = cl.load_balanced_view() # Create a load balanced view that we will use later.
Then we initialize the pylab inline in each engine. Note that we use the %px IPython cell magic to run the code in the cell for each engine.
In [2]:
%%px
%pylab inline
from IPython.display import clear_output
Similarly, we set the Python path and import some libraries at each engine.
In [36]:
%%px
# xxxxxxxxxx Add the parent folder to the python path. xxxxxxxxxxxxxxxxxxxx
import sys
import os
sys.path.append('../')
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
import numpy as np
Because there is already an Alamouti simulator implemented in the "apps" folder of PyPhysim, we simple import it here and set the desired simulation parameters.
In [4]:
%%px
from pyphysim.comm.modulators import BPSK, QAM
from apps.simulate_mimo import AlamoutiSimulationRunner
In [5]:
%%px
sim = AlamoutiSimulationRunner("mimo_alamouti_config_file.txt")
sim.NSymbs = 300
sim.rep_max = 10000
sim.params.add('SNR', np.linspace(0, 25, 11))
#sim.modulator = QAM(16)
#sim.Nr=1
sim.update_progress_function_style = None
# max_bit_errors is used in the _keep_going method to stop the
# simulation earlier if possible. We stop the simulation if the
# accumulated number of bit errors becomes greater then 0.1% of the
# total number of simulated bits
sim.max_bit_errors = sim.rep_max * sim.NSymbs * 0.001
sim.clear()
We still need to set the self.modulator parameter with the desired modulator as well as the self.Nr parameter with the desired number of receive antennas. These values will differ at each engine. After that, we can call the simulate method to perform the actual simulation.
Lets create a function that sets the modulator and number of receive antennas in the SimulationRunner object, calls its simulate method and then return the object. This is so that we can use the map method of a load_balanced_view.
In [6]:
# Note that we run this on the local instance, instead of in the remote engines. That is, we do not use the %%px cell magic here.
def set_params_and_simulate(params_list):
"""params_list must be a list with two elements, the modulator object, and the number of receive antennas."""
modulator, Nr = params_list
sim.modulator = modulator
sim.Nr = Nr
sim.simulate()
return sim
Now lets create all the combination of modulator and number of receive antennas.
In [7]:
# xxxxxxxxxx Add the parent folder to the python path. xxxxxxxxxxxxxxxxxxxx
import sys
import os
sys.path.append('../')
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
from pyphysim.comm.modulators import BPSK, QAM
combinations = [[QAM(16), 1], [QAM(16), 2], [BPSK(), 1], [BPSK(), 2]]
Now we can call the map_sync method of the balanced view to perform the simulations in parallel.
In [8]:
from pyphysim.util.misc import pretty_time
from time import time, sleep
tic = time()
results = bview.map_sync(set_params_and_simulate, combinations)
toc = time()
print "Total elapsed time: {0}".format(pretty_time(toc-tic))
Since we used the map_sync method, then results will be a list containing the SimulationRunner objects (since set_params_and_simulate returns the SimulationRunner object)
Therefore, lets get the individual SimulationRunner objects.
In [9]:
sim1, sim2, sim3, sim4 = results
print "Elapsed time sim1: {0}".format(sim1.elapsed_time)
print "Elapsed time sim2: {0}".format(sim2.elapsed_time)
print "Elapsed time sim3: {0}".format(sim3.elapsed_time)
print "Elapsed time sim4: {0}".format(sim4.elapsed_time)
With the SimulationRunner objects we can now get the results for each configuration.
Lets get the results for 16-QAM modulation
In [31]:
def get_data_to_be_plotted(sim):
SNR = sim.params['SNR']
ber = np.array([r.get_result() for r in sim.results['ber']])
ser = np.array([r.get_result() for r in sim.results['ser']])
return SNR, ber, ser
SNR, ber1, ser1 = get_data_to_be_plotted(sim1)
SNR, ber2, ser2 = get_data_to_be_plotted(sim2)
and now the results for BPSK modulation
In [33]:
SNR, ber3, ser3 = get_data_to_be_plotted(sim3)
SNR, ber4, ser4 = get_data_to_be_plotted(sim4)
Now we have what we need to plot everything.
In [48]:
from matplotlib import pyplot as plt
fig, ax = plt.subplots(1, 2, figsize=(16,5))
ax1, ax2 = ax
# Plot the 16-QAM results
ax1.semilogy(SNR, ber1, '-k*', label='NTx=2, NRx=1')
ax1.semilogy(SNR, ber2, '-m*', label='NTx=2, NRx=2')
ax1.set_title('BER for {0} modulation with Alamouti (Rayleight channel)'.format(sim1.modulator.name))
ax1.set_xlabel('SNR')
ax1.set_ylabel('Error')
ax1.legend()
ax1.grid()
# Plot the BPSK results
ax2.semilogy(SNR, ber3, '-k*', label='NTx=2, NRx=1')
ax2.semilogy(SNR, ber4, '-m*', label='NTx=2, NRx=2')
ax2.set_title('BER for {0} modulation with Alamouti (Rayleight channel)'.format(sim3.modulator.name))
ax2.set_xlabel('SNR')
ax2.set_ylabel('Error')
ax2.legend()
ax2.grid()
plt.show()
In [ ]:
#%qtconsole