El uso de herramientas de programación como Matlab permiten que el desarrollo de simulaciones se realice de una manera muy práctica, dejando que el programador sólo se enfoque a solucionar el problema y no batalle en la implementación del algoritmo. Matlab es un lenguaje de alto nivel que permite que usuarios con conocimientos básicos de programación, puedan implementar cualquier algoritmo de una manera simple y rápida, así mismo, cuenta con múltiples herramientas gráficas que ayudan en la interpretación de los resultados. Una alternativa para el uso de herramientas enfocadas a la investigación es SciPy o Python Científico. SciPy es una biblioteca de Python que permite realizar operaciones especializadas en un lenguaje de programación convencional, facilitando así el desarrollo de algoritmos y optimizando los tiempos de ejecución, de manera similar a Matlab.

Justificación

Una de las características principales de Matlab es el manejo de Matrices, de hecho, su nombre se deriva de la palabra Laboratorio de Matrices. El uso de matrices y vectores permite manejar estructuras de datos mas complejas y realizar operaciones matemáticas que en un lenguaje de programación convencional requeriría varias líneas de código, por ejemplo, una simple multiplicación de matrices, donde en Matlab se realiza por un comando A*B, en otro lenguaje tendríamos que realizar (a groso modo) un ciclo For que recorra cada renglón de la matriz A y la multiplique por la columna de la matriz B y la suma de cada multiplicación colocarla en un nuevo arreglo en la posición que le corresponde.

Aunque Matlab es una herramienta poderosa y popular, esta no es de libre distribución debido a que su código es propietario; por lo que es limitado a la disponibilidad y tipo de licencias con que se disponga en el centro de trabajo. Una alternativa viable puede ser el uso de herramientas de código abierto, existen diversos proyectos que son compatibles incluso a nivel de código y manejo de archivos y datos, tal es el caso de Octave, que es un proyecto donde los archivos desarrollados en Matlab funcionan sin tener que cambiar el lenguaje en que fue programado, aunque existen algunas funciones especializadas no estan implementadas en Octave.

Aún así, la tendencia en algunos lenguajes de programación de libre distribución es ser multipropósitos, esto se logra con la ayuda de bibliotecas especializadas; Por ejemplo, Python, a través de SciPy, puede manipular datos en forma vectorial o matricial permitiendo una implementación sencilla del algoritmo y el cálculo de las operaciones matemáticas, ya que esta incluye funciones que van desde matemáticas básica hasta avanzada.

SciPy o Scientific Python es una biblioteca de Python, que junto a Numpy y MatPlotLib, proporcionan las herramientas necesarias para trabajar en el ambiente de la investigación y educación e incluso en el de desarrollo. En el caso de las telecomunicaciones, permite hacer transmisores OFDM con la ayuda de la función de transformadas de Fourier (fft, ifft), manejos de datos complejos y operaciones entre ellos (absolute, norm, sum) y generación de datos aleatorios con una distribución específica (randn para una distribución normal) o matrices de unos y ceros (ones, zeros). SciPy ofrece una wiki con una lista de comandos básicos de Matlab y su equivalente en SciPy.

MatPlotLib es una herramienta visual que permite mostrar los datos calculados en las simulaciones en diferentes tipos de gráficas, una muy utilizada en telecomunicaciones para el cálculo de BER es semilogy.

La instalación de SciPy en una distribución Linux basada en Debian/Ubuntu se realiza a través de la terminal de comandos.

sudo apt-get install python-numpy python-scipy python-matplotlib ipython ipython-notebook python-pandas python-sympy python-nose python-tk

Un Ejemplo de un código desarrollado en Python con SciPy se muestra a continuación:

#Programa DFTS-OFDM

#Para ejecutar se requiere de scipy y matplotlib
#Desde una terminal ejecute:
# sudo apt-get install python-numpy python-scipy python-matplotlib ipython ipython-notebook python-pandas python-sympy python-nose python-tk
#La simulacion la puede correr desde una terminal
# python DFTSOFDM.py

#Carga de bibliotecas
import scipy
import numpy
import matplotlib.pyplot

numpy.set_printoptions(precision=4,suppress=True)

#Datos de inicializacion
print "Inicializando parametros de simulacion"
snr = numpy.arange(0,33,8)
taps = 1.
itera = 1000.
paq = numpy.shape(snr)[0]*itera
datasize = 768
gx = "04C11DB7"
polsize = 32
dfts = 192
ofdm = 2048
cp = ofdm/4
pilot = 4
pilot_carriers = [-61,-31,31,61]
dfts_carriers = [x for x in (range(-((dfts+pilot)/2),0,1) + range(1,(dfts+pilot)/2+1,1)) if x not in pilot_carriers]

### TRANSMISOR ###

#Generacion de datos aleatorios
print "Generando bits de datos aleatorios"
datab = numpy.random.randint(2, size=(datasize-polsize)*paq)
datab = numpy.reshape(datab,(paq,-1))

#Etapa CRC
print "Calculando CRC"
pol = numpy.array([int(y) for x in bin(int(str(gx), 16))[2:].zfill(polsize).rjust(polsize+1,'1') for y in x])
datazeros = numpy.concatenate((datab,numpy.zeros((paq,polsize),dtype=numpy.int)),1)
datacrc = datazeros.copy()

#Calculo CRC
for j in range(scipy.shape(datazeros)[0]):
	for i in range(scipy.shape(datazeros)[1]):
		if datazeros[j,i]==1 and i+scipy.shape(pol)[0]-1 < scipy.shape(datazeros)[1]:
			datazeros[j,i:scipy.shape(pol)[0]+i] = datazeros[j,i:scipy.shape(pol)[0]+i]^pol

#Datos con CRC
datacrc = datacrc|datazeros

#Modulador 16-QAM
print "Datos en el modulador digital"
dataqam = numpy.reshape(datacrc,(-1,4))
dataqam [:,[0,1]] = dataqam [:,[0,1]]*2-1
dataqam [:,[2,3]] = -(dataqam [:,[2,3]]*2-3)
datam = dataqam[:,0]*dataqam[:,2] + dataqam[:,1]*dataqam[:,3]*1j

#DFTS
print "Aplicando DFTS"
datadfts = numpy.reshape(datam,(-1,dfts))

for i in range(scipy.shape(datadfts)[0]):
	datadfts[i,0:scipy.shape(datadfts)[1]] = numpy.fft.fft(datadfts[i,0:scipy.shape(datadfts)[1]])

#OFDM
print "En el transmisor OFDM"
#Generacion de ceros y pilotos
datapilot = numpy.reshape(numpy.random.randint(2,size=paq*pilot)*2-1,(paq,-1))
dataofdm = numpy.zeros((paq,ofdm),dtype=numpy.complex)

#Asignacion de portadoras
dataofdm[:,pilot_carriers] = datapilot
dataofdm[:,dfts_carriers] = datadfts

#IFFT
for i in range(scipy.shape(dataofdm)[0]):
	dataofdm[i,0:scipy.shape(dataofdm)[1]] = numpy.fft.ifft(dataofdm[i,0:scipy.shape(dataofdm)[1]])

#Prefijo ciclico
dataofdm = numpy.concatenate((dataofdm[:,ofdm-cp:ofdm],dataofdm),1)

### CANAL ###
#Multitrayectoria
print "Agregando multitrayectorias del canal"
h = 1/numpy.sqrt(2*taps)*(numpy.random.randn(scipy.shape(dataofdm)[0],taps) + numpy.random.randn(scipy.shape(dataofdm)[0],taps)*1j)
h = numpy.reshape((numpy.tile(1./(numpy.arange(taps)+1),scipy.shape(dataofdm)[0])),(-1,taps))*h
H = numpy.zeros((paq,ofdm+cp),dtype=numpy.complex)
datacanal = numpy.zeros((paq,ofdm+cp),dtype=numpy.complex)

for i in range(scipy.shape(dataofdm)[0]):
	H[i] = numpy.fft.fft(h[i]/numpy.linalg.norm(h[i]),ofdm+cp)
	datacanal[i] = numpy.fft.fft(dataofdm[i])

datacanal = datacanal*H;

for i in range(scipy.shape(dataofdm)[0]):
	datacanal[i] = numpy.fft.ifft(datacanal[i])    

#AWGN
print "Sumando AWGN a los datos"
ebno = numpy.sqrt(numpy.reshape(numpy.repeat(10**(snr/10.),scipy.shape(datacanal)[1]*scipy.shape(datacanal)[0]/scipy.shape(snr)[0]),(-1,scipy.shape(datacanal)[1])))
awgn = (numpy.random.randn(scipy.shape(datacanal)[0],scipy.shape(datacanal)[1]) + numpy.random.randn(scipy.shape(datacanal)[0],scipy.shape(datacanal)[1])*1j)/(numpy.sqrt(2)*ebno)
datacanal = datacanal + awgn

### RECEPTOR ###
print "En el receptor OFDM"
#Remueve el prefijo ciclico
dataiofdm = datacanal[:,cp:ofdm+cp].copy()

#FFT
for i in range(scipy.shape(dataiofdm)[0]):
	dataiofdm[i,0:scipy.shape(dataiofdm)[1]] = numpy.fft.fft(dataiofdm[i,0:scipy.shape(dataiofdm)[1]])

#Ecualizador
print "Ecualizando los datos recibidos"
dataiofdm = dataiofdm[:,dfts_carriers].copy()/H[:,dfts_carriers]

#IDFTS
print "Calculando IDFTS"
for i in range(scipy.shape(dataiofdm)[0]):
	dataiofdm[i,0:scipy.shape(dataiofdm)[1]] = numpy.fft.ifft(dataiofdm[i,0:scipy.shape(dataiofdm)[1]])

#Demodulador 16-QAM
print "Demodulador digital"
datadem = numpy.reshape(dataiofdm,(1,-1))
datadem = numpy.transpose(numpy.vstack((numpy.real(datadem)>0,numpy.imag(datadem)>0,numpy.absolute(numpy.real(datadem))<2,numpy.absolute(numpy.imag(datadem))<2))*1)

#Recepcion para calculo de CRC
print "Calculo de CRC Check"
datarcrc = numpy.reshape(datadem,(paq,-1))
datarx = datarcrc.copy()

#Calculo de CRC
for j in range(scipy.shape(datarcrc)[0]):
	for i in range(scipy.shape(datarcrc)[1]):
		if datarcrc[j,i]==1 and i+scipy.shape(pol)[0]-1 < scipy.shape(datarcrc)[1]:
			datarcrc[j,i:scipy.shape(pol)[0]+i] = datarcrc[j,i:scipy.shape(pol)[0]+i]^pol

#Eliminacion del CRC y paquetes incorrectos
datar = datarx[:,0:datasize-polsize].copy()

#Contador de paquetes correctos de acuerdo al CRC
print "Calculando PER"
per = numpy.sum(numpy.reshape(((numpy.sum(datarcrc,1)!=0)*1),(-1,itera)),1)/itera

#Comparacion entre datab y datar y promedia errores de acuerdo a iteraciones
print "Calculando BER"
ber = numpy.sum((numpy.reshape(numpy.sum(numpy.absolute(datar-datab), axis=1),(-1,itera))),1)/(itera*(datasize-polsize))

print "SNR %BER %PER"
print numpy.vstack([snr,ber*100,per*100]).T
matplotlib.pyplot.semilogy(snr,per,snr,ber)
matplotlib.pyplot.legend(["PER","BER"])
matplotlib.pyplot.show()

@viktor_ivan