capítulo 7. anexosbibing.us.es/proyectos/abreproy/11984/fichero/volumen+8... · y que gnu radio...
TRANSCRIPT
Iván Pinar Domínguez
113
Capítulo 7. ANEXOS
7.1 ANEXO I: Instalación manual de GNU Radio
Se recomienda realizar la instalación semiautomática tal y como se indica en el capítulo 2.
No obstante en este apartado se mostrará cómo realizar la instalación manual de GNU Radio,
que es algo laboriosa. Lo primero que hay que hacer es ubicar los paquetes de GNU Radio los
cuales se pueden encontrar en www.gnu.org/software/gnuradio (2.x tarball) o utilizando CVS
(Concurrent Versions System), lo cual se realizará más adelante. Los paquetes son los
siguientes:
• gnuradio-core: El núcleo de las librerías.
• gnuradio-examples: Ejemplos de GNU Radio.
• gr-audio-oss: Soporte para tarjetas de sonido que utilizan (Open Sound System).
• gr-audio-alsa: Soporte para tarjetas de sonido que utilizan ALSA (Advanced Linux
Sound Architecture).
• gr-usrp: Librerías que unen a GNU Radio con el Universal Software Radio Peripheral.
• gr-wxgui: wxPython basado en herramientas GNU, incluye un oscilocopio y FFT´s.
• gr-how-to-write-a-block: Incluye ejemplos de cómo escribir un bloque.
• usrp: Soporte para la tarjeta USRP.
Antes de instalar gnuradio-core es necesario tener algunos paquetes preinstalados. Estos
paquetes son los siguientes:
FFTW (Fastest Fourier Transform in the West): Ésta es una subrutina en C que es capaz
de computar transformadas discretas de Fourier en una o más dimensiones, el tamaño de la
entrada es arbitrario y maneja datos complejos y reales. FFTW es software libre por lo que se
convierte en una librería de FFT para cualquier aplicación.
Se puede obtener de: http://www.fftw.org/download.html
Cppunit: Es una unidad de prueba para chequear bloques en C++.
Se puede conseguir de: https://sourceforge.net/project/showfiles.php?group_id=11795
Software Defined Radio: USRP y GNU Radio
114
SWIG (Simplified Wrapper and Interface Generator): Son herramientas para el
desarrollo de software que conectan programas escritos en C o C++ con una gran variedad de
lenguajes de programación de alto nivel. SWIG es utilizado con diferentes tipos de lenguaje
como Python.
Se puede descargar de: http://sourceforge.net/projects/swig/
Numarray y Numpy: Son dos módulos de Python necesarios para el funcionamiento de
GNU Radio. Éstos son utilizados para computo numérico y están disponibles en:
http://sourceforge.net/projects/numpy
wxPython: Son herramientas para el lenguaje de programación Python, y permite crear
programas con funciones e interfaces gráficas de forma simple.
Se puede conseguir de: https://sourceforge.net/project/showfiles.php?group_id=10718
La instalación de los programas antes mencionados es ciertamente complicada ya que éstos
también requieren algunos programas preinstalados, lo que vuelve al proceso bastante confuso.
Resulta de ayuda el Synaptic Package Manager de Ubuntu, el cual se encuentra en:
SystemAdministrationSynaptic Package Manager
Al haber dado de alta, en un principio, los repositorios donde se encuentran las librerías
necesarias, ahora simplemente hay que instalarlas; para esto es necesario buscar la librería
requerida con el botón de “search”, seleccionarla para instalación y aplicar la instalación con el
botón “apply”.
También se debe instalar las siguientes librerías adicionales:
• automake1.8
• gcc/g++-3.4
• g++-3.4
Después de instalar gcc y g++ se debe crear una liga simbólica con los siguientes comandos
ejecutados en una Terminal:
# cd /usr/bin
# sudo ln -s gcc-3.4 gcc
# sudo ln -s g++-3.4 g++
• python2.6
• python2.6-dev
• libtool (1.5)
• wxpython2.5.3
En caso de no ser encontrado wxpython por el Synaptic Package Manager es necesario
ejecutar en una Terminal los siguientes comandos:
# apt-get update
# apt-get install wxpython2.5.3
• python2.6-numeric • libasound2 para ALSA
• libasound2-dev para ALSA
• libusb-dev
• sdcc
• bjam
• libboost-dev
• libboost-python1.31.0
• libboost-python-dev
• fftw3
• fftw3-dev
• cppunit
• libcppunit-dev
• libcppunit-1.10-2
Iván Pinar Domínguez
115
Las únicas librerías que no se encuentran en los repositorios son las de SWIG por lo que es
necesario instalarlo manualmente, para esto hay que descargarlo de
“http://sourceforge.net/projects/swig/” donde además se puede obtener la última versión.
El archivo descargado se encuentra en formato comprimido swig-*.tar.gz por lo que es
necesario descomprimirlo. Para esto es necesario utilizar una Terminal y situarse sobre el
directorio donde se encuentra el archivo comprimido. En la Terminal y ya en ese directorio se
debe escribir el siguiente comando:
# tar zxvf swig-1.3.24.tar.gz
Con esto se obtendrá una carpeta con los archivos necesarios de SWIG. En la misma
terminal habrá que situarse en esta nueva carpeta y para realizar la instalación es necesario
ahora ejecutar los siguientes comandos esperando a que cada uno termine de ejecutarse antes
de correr el siguiente:
# ./configure
# make
# make check
# make install
Ahora para obtener los paquetes de GNU Radio se usará CVS, que es una forma de
descargar los paquetes en su última versión sin necesidad de hacerlo desde una página de
Internet. Para esto se debe ejecutar los siguientes comandos en una Terminal:
# export CVS_RSH="ssh"
# cvs -z3 -d:ext:[email protected]:/cvsroot/gnuradio co -P gr-build
Con estos comandos se creará una carpeta llamada gr-build con los archivos necesarios para
instalar a continuación los paquetes de la arquitectura GNU Radio.
Ahora se descargarán los paquetes en esta carpeta que se creó, para esto habrá que situarse
sobre esa carpeta y teclear lo siguiente en la línea de comandos de la Terminal:
# cvs -z3 -d:ext:[email protected]:/cvsroot/gnuradio co -P gnuradio-core
# cvs -z3 -d:ext:[email protected]:/cvsroot/gnuradio co -P gnuradio-examples
# cvs -z3 -d:ext:[email protected]:/cvsroot/gnuradio co -P gr-audio-alsa
# cvs -z3 -d:ext:[email protected]:/cvsroot/gnuradio co -P gr-audio-oss
# cvs -z3 -d:ext:[email protected]:/cvsroot/gnuradio co -P gr-usrp
# cvs -z3 -d:ext:[email protected]:/cvsroot/gnuradio co -P gr-wxgui
# cvs -z3 -d:ext:[email protected]:/cvsroot/gnuradio co -P gr-howto-write-a-
block
# cvs -z3 -d:ext:[email protected]:/cvsroot/gnuradio co -P gr-gsm-fr-vocoder
# cvs -d:pserver:[email protected]:/cvsroot/opensdr login
# cvs -z3 -d:pserver:[email protected]:/cvsroot/opensdr co -P usrp
Para realizar la instalación final se debe ejecutar lo siguiente en la Terminal en el
directorio de gr-build:
# ./checkout
# ./for-all-dirs ../buildit 2>&1 | tee make.log
El proceso debe durar algunos minutos y de acuerdo a lo hecho anteriormente no debe haber
ningún error, en caso de existir alguno el sistema nos avisara si hace falta algún archivo o
librería, de no ser así es necesario revisar el proceso nuevamente.
Software Defined Radio: USRP y GNU Radio
116
Ahora se debe obtener las actualizaciones para el USRP, lo único que hay que hacer es
descarga este archivo de www.gnuradio.org/software/gnuradio descomprimirlo como se hizo con
SWIG y copiar los archivos que no se encuentren en la carpeta de usrp en gr-build. Ahora para
comprobar que todo el proceso se realizó correctamente se ejecutará un programa de ejemplo
que simula el tono que da una línea telefónica, para esto, desde una Terminal ubicarse en la
carpeta gr-build y teclear lo siguiente:
# cd gnuradio-examples/python/audio/
# ./dial_tone.py
Si se escucha un tono como el mencionado anteriormente significa que el ejemplo funciona,
y que GNU Radio está instalado correctamente. En caso de no encontrar el archivo dial_tone.py
se puede crear a través del código que se mostrará en el anexo 7.3.
Como la instalación de GNU Radio se ha realizado directamente desde CVS, hay un archivo
(usrp.fpga.rbf) que se debe extraer del paquete usrp-*.*.tar.gz de la siguiente manera:
# mkdir tmp
# cd tmp (descargar el archivo comprimido a esta carpeta)
# tar xvvfz usrp-*.*.tar.gz
# cp usrp-*.*/fpga/rbf/usrp_fpga_rev2.rbf /usr/local/share/usrp/rev2/usrp_fpga.rbf
# cd ..
# rm -rf tmp
Con esto ya se tienen todos los archivos necesarios en el lugar adecuado y se puede comenzar
a ejecutar programas utilizando el USRP y GNU Radio.
7.2 ANEXO II: Análisis detallado de funcionamiento USRP
Convertidores AD
El USRP tiene 4 convertidores ADC de alta velocidad, cada uno a 12 bits por muestra y 64
millones de muestras por segundo con lo que se puede digitalizar una banda de 32MHz
teóricamente. Si se muestrea una señal de ancho de banda mayor que 32MHz se introducirá
aliasing en el sistema.
El rango completo del ADC es de 2V pico a pico y tiene una entrada diferencial de 50Ω, lo
que significa 13dBm o 20mW de potencia máxima. Antes del ADC se encuentra un
amplificador de potencia programable (PGA) para amplificar la señal de entrada y utilizar el
rango completo en el caso de que la señal sea débil cuyo rango es de 0 a 20 dB. La tarjeta
secundaria también tendrá una amplificación (programable en general), debido a ello, cuando se
especifique la ganancia del USRP, será la suma de ambas contribuciones. Por ejemplo, si se
indica una ganancia de 50 dB, el PGA se ajustará a 20 dB y la tarjeta secundaria a 30 dB (al
igual ocurre en sentido de transmisión si la ganancia es ajustable). Se verá más claro con la
figura resumen al final de esta sección.
Convertidores DA
El USRP tiene 4 convertidores DAC de alta velocidad para transmisión, cada uno a 14 bits
por muestrea y 128 millones de muestras por segundo, por lo que la frecuencia de Nyquist es de
64MHz. Sin embargo, en la práctica la frecuencia de entrada máxima a la que trabaja el DAC es
de 44 MHz como se explicará posteriormente.
Iván Pinar Domínguez
117
Los DAC´s pueden suministrar 1V pico a pico a una carga diferencial de 50Ω esto es 10dBm
o 10mW. Aparece también un PGA conectado después del DAC para aumentar la ganancia 20
dB (aunque la ganancia puede ser mayor si tenemos en cuenta la amplificación que puede
introducir la Daughterboard).
Procesador e Interfaces
El procesador utilizado por el USRP es una FPGA Altera Cyclone EP1C12. Comprender el
funcionamiento de la FPGA es importante para utilizar GNU Radio ya que la tarea de éste es
realizar procesos matemáticos de las señales en la banda que se precise y reducir las tasas de
muestreo de datos en la interface USB 2.0. Los ADC y DAC´s están conectados a la FPGA y
éste a su vez se conecta a un chip de interface USB 2.0, el Cipres FX2. En la interfaz USB todas
las muestras son de tipo signed integer a 32 bits (16 I y 16 Q). La FPGA incluye 4 convertidores
digitales de bajada (DDC) como muestra la Figura 7-1 para disminuir la tasa de muestreo
(diezmado) permitiendo 1, 2 ó 4 canales independientes de recepción y cada DDC tiene dos
entradas, I (en fase) y Q (en cuadratura). Cada ADC puede ser ruteado a cualquiera de las
entradas I y Q de los 4 DDC (incluso una salida del ADC puede ser ruteado a más de un DDC).
Para la transmisión se tienen convertidores digitales de subida (DUC) como aparece en la
Figura 7-3, que llevan a cabo un proceso de interpolación contenidos fuera de la FPGA en el
chip AD9862 CODEC, de esta manera se aprovecha la tasa de muestreo de 128 MHz del DAC
en lugar de estar limitado por los 64 MHz de la frecuencia de reloj de la FPGA. En teoría, se
podría transmitir señales de hasta 64 MHz de frecuencia máxima, aunque en la práctica el límite
está en 44 MHz debido al filtro paso banda del AD9862. La tarjeta secundaria posteriormente
elevará la señal generada a la frecuencia deseada.
Los canales múltiples para la recepción deben todos tener la misma tasa de transmisión de
datos, al igual que los canales de transmisión que deben tener el mismo régimen binario el cual
debe ser distinto del de recepción. Un detalle a tener en cuenta es que, internamente, la FPGA
diezma por un factor de al menos 4, que será el mínimo valor de diezmado. Este factor lo limita
la tasa de datos por el USB (32 MB/s), ya que si se usan 8 bits para la componente I y otro 8 bits
para la componente Q, un factor de diezmado por 4 produce una tasa de 16 MS/s (64 MS/s, la
máxima tasa de muestreo de entrada dividida por 4) por lo que la tasa de datos por la interfaz
USB será de 32 MB/s (16 MS/s * 2 Bytes). En el caso de utilizar 16 bits por componente que
será lo habitual para aprovechar los 14/12 bits a los que trabaja el DAC/ADC, el factor de
diezmado mínimo será 8 siguiendo el mismo razonamiento, esto es, como la velocidad máxima
en el USB es de 32 MB/s, entonces el diezmado tiene que duplicarse si pasamos de 8 a 16 bits
por componente. El factor de diezmado máximo admisible es 512 y por tanto el rango de valores
posibles está entre 8 y 512 y sólo valores pares para que la tasa de muestras por la interfaz USB
sea un número entero, además se recomienda que el diezmado sea una potencia de 2 por
eficiencia de algunos algoritmos como la FFT. Igualmente, el factor de interpolado se debe
encontrar entre 8 y 512.
En la Figura 7-1 se muestra un diagrama a bloques de la trayectoria de recepción desde la
antena hasta el DDC.
Software Defined Radio: USRP y GNU Radio
118
MULTIPLEXOR
ADC 0
ADC 1
ADC 2
ADC 3
Tarjeta Secundaria
Receptora
RX A
RX B
DDC 0
I
Q
DDC 1
I
Q
DDC 2
I
Q
DDC 3
I
Q
USB
Figura 7-1 Multiplexor en el USRP, donde I es la señal en fase y Q la señal en cuadratura
El multiplexor de la Figura 7-1 es un circuito seleccionador, que determina qué ADC se
conecta a cada DDC. Se puede controlar este MUX utilizando el método en Python llamado
usrp.set_mux(), funcionalidad que se pierde al utilizar GNU Radio Companion o el entorno
Matlab.
Como se ha comentado en el apartado 3.2.2, si se supone el diseño de un receptor de FM en
donde el ancho de banda de una emisión es de 200 KHz, se puede seleccionar un factor de
diezmado de 200 con lo que la tasa de recepción (y ancho de banda) a través del USB será de
64MS/s / 200 = 320 KS/s (y por tanto 320 KHz) el cual satisface los 200 KHz de ancho de
banda sin perder información.
Es posible seleccionar la frecuencia IF del DDC utilizando el método en Python de
usrp.set_rx_frec(), y para seleccionar el factor de diezmado usrp.set_decim_rate(), el cual puede
ir de 4 a 512. Hay que tener en cuenta que normalmente se utilizará una frecuencia IF de 0, es
decir, banda base. Al seleccionar la frecuencia a la que se desea operar con el método
usrp.tune(), la tarjeta secundaria intentará ajustarse al máximo a esa frecuencia aunque es
difícilmente será capaz de ajustarse exactamente, para solucionarlo en el DDC antes del
diezmado se centra el espectro entre –fs/2 y fs/2. En la siguiente figura se aprecia la
implementación:
Componente Q
ADC
Componente I
ADC
NCO
Generador
Seno/Coseno
Centra la banda de frecuencias
-fs/2 a fs/2
cos sin
Filtro Paso Bajo
Diezmador (N)
I
Q
Tasa de muestras = 64 MHz Tasa de muestras = 64 MHz/N
16 bits/muestra de
cada componente a
través del USB
Figura 7-2 Diagrama interno del DDC
El NCO (Numerically Controlled Oscillator) será controlado por los bloques encargados de
la sincronización de la arquitectura GNU Radio, cosa que no puede realizarse en Matlab, de ahí
los problemas de sincronización que se han comentado en el apartado 6.2.2.
Iván Pinar Domínguez
119
En cuanto a las muestras por la interfaz USB, cuando se tienen múltiples canales (más de
cuatro) los canales son multiplexados. Por ejemplo, con cuatro canales, la secuencia enviada a
través del USB será I0 Q0 I1 Q1 I2 Q2 I3 Q3 I0 Q0 I1 Q1, etc.
Finalmente las señales I y Q entran a la computadora vía USB y se procesa la señal banda
base en software.
Para la transmisión todo sucede de forma similar, con la diferencia de que el proceso es
contrario. Se envía una señal I y Q en banda base de la computadora al USRP. El convertidor
digital de subida (DUC) interpola la señal y la envía a través del DAC. El factor de interpolado
será 128 MHz dividido por la tasa de muestreo de los datos de entrada al USRP.
Una vez vistos todos los conceptos del USRP, se muestra a continuación un diagrama de
bloques general de la tarjeta madre más detallado que el presentado en apartados anteriores:
J6
66
ADC 0
ADC 1
J6
68
ADC 2
ADC 3
MUX
DDC 0 / M
DDC 0 / M
DDC 0 / M
DDC 0 / M
RX
FIF
O
Co
ntr
ola
do
r F
X2
US
B 2
.0
Entrelazado
J6
67
J6
69
DAC 0
DAC 1
DAC 2
DAC 3
DUC
I
Q
DUC
I
Q
HBF
4
HBF
4
DEMUX
I I
I I
Q Q
Q Q
CIC
N
I
Q
CIC
N
I
Q
TX
FIF
O
Desentrelazado
I
Q
I
Q
I
Q
I
Q
I
Q
I
Q
I
Q
I
Q
I
Q
I
Q
Rx A
Rx B
Tx A
Tx B
FPGA
Altera Cyclone
ADC/DAC
Chip AD9862BST
Motherboard
Figura 7-3 Diagrama de bloques detallado del USRP
Por último, se mostrará una figura resumen del proceso del proceso de recepción desde la
antena hasta la interfaz USB en cuanto a muestreos y ganancias se refiere. Hay que tener en
cuenta que normalmente la frecuencia intermedia será 0, por ello la tarjeta secundaria intentará
sintonizarse al máximo a la radiofrecuencia deseada. El proceso de transmisión es simétrico.
Software Defined Radio: USRP y GNU Radio
120
Motherboard
Digital Down Converter
Tarjeta Secundaria
RF Front End
fRF+Δf
A
D
64 MS/s
NCO
N USB
fRF
B
Δf
Δf
B
B
Δf
Δf
B
Δf ΔF+64MHz
B
Δf
B
0
B
64MHz
B
0 64MHz/N
PGA
B
Figura 7-4 Proceso de recepción
Como puede verse, la señal de radiofrecuencia con ancho de banda B es captada por la antena
y llevada a la tarjeta secundaria, donde se amplifica y se intenta llevar la señal a banda base
aunque habrá una pequeña desviación. Después la banda deseada entra en la Motherboard donde
se vuelve a amplificar, se digitaliza a 64 MS/s (obteniendo el espectro repetido cada 64 MHz) y
gracias al NCO se elimina la desviación de frecuencia obteniendo la señal en banda base.
Finalmente se realiza un diezmado por N, por lo que la tasa de muestras que se enviará por la
interfaz USB será de 64 MS/s / N.
7.3 ANEXO III: Programación directa en Python
En este anexo se analizará línea por línea el código del módulo dial_tone.py que se muestra a
continuación:
from gnuradio import gr
from gnuradio import audio
from gnuradio.eng_option import eng_option
from optparse import OptionParser
class ejemplo_tono(gr.top_block):
def __init__(self):
gr.top_block.__init__(self)
parser = OptionParser(option_class=eng_option)
parser.add_option("-O", "--audio-output", type="string", default="",
help="pcm output device name. E.g., hw:0,0 or /dev/dsp")
parser.add_option("-r", "--sample-rate", type="eng_float", default=48000,
help="set sample rate to RATE (48000)")
(options, args) = parser.parse_args ()
if len(args) != 0:
parser.print_help()
raise SystemExit, 1
sample_rate = int(options.sample_rate)
ampl = 0.1
Iván Pinar Domínguez
121
src0 = gr.sig_source_f (sample_rate, gr.GR_SIN_WAVE, 350, ampl)
src1 = gr.sig_source_f (sample_rate, gr.GR_SIN_WAVE, 440, ampl)
dst = audio.sink (sample_rate, options.audio_output)
self.connect (src0, (dst, 0))
self.connect (src1, (dst, 1))
if __name__ == '__main__':
try:
ejemplo_tono().run()
except KeyboardInterrupt:
pass
Si las primeras líneas, se inician con #, son únicamente comentarios y no afectan a la lógica
del programa. Es necesario, para comprender las siguientes líneas, hablar sobre los módulos y
paquetes en lenguaje Python.
Un módulo es un programa realizado en Python con un sufijo “.py” y contiene un programa
con alguna función específica la cual está disponible como una variable global “__name__”, esta
función puede ser importada dentro de otro modulo en cualquier nivel. Un paquete es un
conjunto de módulos que realizan funciones similares. Para que Python maneje estas carpetas
como paquetes es necesaria la función “__init__.py”, un paquete puede contener módulos y sub-
paquetes. La estructura dentro del programa para módulos y paquetes es del tipo x.y, donde y es
un módulo contenido en el paquete x, por lo que si en el programa tenemos “gr.sig_source_f”
significa que el módulo sig_source_f.py se encuentra en el paquete gr. Estos paquetes y módulos
se encuentran en: “/usr/lib/python2.x/dist-packages”.
Se pasará a comentar el ejemplo. Al inicio del programa se importan los siguientes módulos:
from gnuradio import gr
from gnuradio import audio
from gnuradio.eng_option import eng_option
from optparse import OptionParser
Con lo que se importa el paquete gr y el módulo audio.py de la carpeta de gnuradio, como se
muestra en la Figura 7-5 además de los otros dos módulos.
Software Defined Radio: USRP y GNU Radio
122
Figura 7-5 Paquete gr y módulo audio.py en la carpeta gnuradio
A continuación se define la clase ejemplo_tono la cual hereda los métodos de la clase
gr.top_block a partir de la siguiente sentencia:
class ejemplo_tono(gr.top_block):
Ahora se especifica la primera función que se ejecutará al crear un objeto de la clase
ejemplo_tono con el argumento self, por lo que servirá para cualquier proceso de inicialización.
Por su parte, el argumento self se utilizará para asignar atributos al objeto creado y poder
acceder posteriormente a esos atributos. Si por ejemplo tenemos una función con un argumento
potencia, para asignar el atributo al objeto escribiríamos self.potencia=potencia, por lo que self
referencia al objeto creado con los atributos correspondientes tal y como se aprecia a
continuación:
def __init__(self):
gr.top_block.__init__(self)
Con gr.top_block.__init__(self) se llama a la función inicial del módulo top_block del
paquete gr y se pasa como argumento el objeto creado.
El siguiente bloque de código se refiere a las opciones que se proponen al usuario para
seleccionar la salida de audio y la tasa de muestreo:
parser = OptionParser(option_class=eng_option)
parser.add_option("-O", "--audio-output",…)
parser.add_option("-r", "--sample-rate", …)
(options, args) = parser.parse_args ()
if len(args) != 0:
parser.print_help()
raise SystemExit, 1
Iván Pinar Domínguez
123
En primer lugar, se crea el objeto parser de la clase OptionParser al que se aplica el método
add_option (definido en la clase OptionParser obviamente) para poder seleccionar la salida de
audio y la tasa de muestreo (si no se definen tomarán los valores por defecto). Si al ejecutar el
fichero se le añade “-O” o bien “-r” se guardará en las variables options y args, en cuyo caso se
imprimirá la ayuda si el número de argumentos no es igual a 0.
Las siguientes instrucciones definen la tasa de muestreo (obtenida de options) y la amplitud:
sample_rate = int(options.sample_rate)
ampl = 0.1
Ahora se crean dos fuentes de señal con el método sig_source_f al que se pasa como
argumentos la tasa de muestreo, la forma de onda, la frecuencia y la amplitud con sample_rate,
gr.GR_SIN_WAVE, 350/440 y ampl respectivamente. Además se define el destino a partir de la
función sink del módulo audio.py pasando como argumentos la tasa de muestreo y la salida de
audio obtenida de la variable options:
src0 = gr.sig_source_f (sample_rate, gr.GR_SIN_WAVE, 350, ampl)
src1 = gr.sig_source_f (sample_rate, gr.GR_SIN_WAVE, 440, ampl)
dst = audio.sink (sample_rate, options.audio_output)
La fuente pertenece a la categoría de bloques. Los bloques están en una interface entre
Python y C++. El bloque utilizado en esta parte es gr.sig_source_f el cual se encuentra en
“/usr/lib/python2.x/dist-packages/gnuradio/gr”.
El módulo audio.py se encuentra en “/usr/lib/python2.x/dist-packages/ gnuradio/audio.py” el
cual es importado desde el inicio. Este módulo envía las señales a la tarjeta de audio para poder
reproducirlas.
Ahora se debe realizar la conexión entre los bloques que se han definido anteriormente para
completar el grafo:
self.connect (src0, (dst, 0))
self.connect (src1, (dst, 1))
Con esto se crea la conexión lógica del grafo uniendo los puertos de entrada y salida (src-
fuente y dst-destino).
La última parte del programa consiste en hacer correr el flujo de datos a través del grafo:
if __name__ == '__main__':
try:
ejemplo_tono().run()
except KeyboardInterrupt:
pass
La primera línea sirve para ejecutar el programa directamente, es decir, a través de una
Terminal con el comando ./dial_tone.py ya que en ese caso la variable __name__ y __main__
serán iguales y se ejecutará el contenido del bucle. En caso de que el módulo que se ha creado
sea llamado por otro módulo, la parte que se encuentra debajo de esta línea será ignorada y
solamente se importará el contenido anterior a ésta. Es decir, esta línea sirve para hacer del
código un módulo ejecutable o un sub-módulo. La siguiente línea es la que llama a la clase
ejemplo_tono y hace que se procese con el método run(), para ello se realiza el intento con try y
permanece a menos que se genere la excepción KeyboardInterrupt (Control+C), en cuyo caso se
ejecuta la orden pass que no hace nada pero se utiliza en caso de que sea necesaria una
sentencia sintácticamente hablando pero que el programa no haga nada.
Software Defined Radio: USRP y GNU Radio
124
El archivo dial_tone.py es un módulo básico para trabajar con GNU Radio, de ahí que es
importante comenzar a programar con éste, ya que además de dar una clara introducción de
cómo se realizan los grafos, bloques y conexiones, introduce a la programación de señales de
audio.
No obstante, recordar que para realizar los diseños existe la herramienta GNU Radio
Companion que evita al programador tener que trabajar directamente sobre el código aportando
una interfaz de interconexión de bloques de procesado de señal.
7.4 ANEXO IV: Códigos Matlab con SDR4All
7.4.1 Ficheros de introducción a SDR4All
Transmisor
%%Prueba SDR4All Transmisor sock=SDR4All_Connect(0,'SlotB','TX'); % USRP #0, slot B SDR4All_SetGain(sock,20); % Ganancia de transmisión máxima SDR4All_SetFreq(sock,2422e6); SDR4All_SetInterpRate(sock,256); % Tasa de muestras y ancho de banda
de 500 KHz (128MHz/256) %Definición de señal a transmitir Basic = kron(ones(6250,1),[ones(10,1);-ones(10,1)]); %125000
muestras 1,-1 alternativamente Vide = zeros(125000,1); Base = [Basic;Vide]; Sig = kron(ones(20,1),Base); % 20 ráfagas, Sig contiene 5e6 muestras Te = (1:length(Sig))/(500e3); % 10 segundos de señal plot(Te,Sig); SDR4All_SendData(sock,Sig);
Receptor
%%Prueba SDR4All Receptor sock=SDR4All_Connect(0,'SlotA','RX'); % USRP #0 Slot A [gain_min,gain_max,gain_step] = SDR4All_GetGain(sock); [freq_min,freq_max] = SDR4All_GetFreq(sock); SDR4All_SetGain(sock,(gain_max+gain_min)/2); SDR4All_SetDecimRate(sock,128); % Tasa de muestras y ancho de banda
de 500 KHz SDR4All_SetFreq(sock,2422e6); [Data] = SDR4All_GetData(sock,5*500e3); % Se reciben 5 segundos Te = (1:length(Data))/(500e3); plot(Te,real(Data));
7.4.2 Ficheros diseño D-QPSK con SDR4All
Función pulso.m (Root Raised Cosine Filter)
function [pt,t]=pulso(Ts,alpha)
t=-5*Ts:Ts/16:5*Ts;
%%Definición del pulso pt = cos((1+alpha)*pi*t/Ts)+Ts*sin((1-alpha)*pi*t/Ts)./(4*alpha*t); pt = pt./(1-(4*alpha*t/Ts).^2); pt = pt.*4*alpha/(pi * sqrt(Ts));
Iván Pinar Domínguez
125
%%Indices de los puntos singulares pos_ceros_den_num=find(t==0); pos_ceros_den1=find(t==Ts/(4*alpha)); pos_ceros_den2=find(t==-Ts/(4*alpha));
%Cálculo singularidades
if length(pos_ceros_den_num)>0 pt(pos_ceros_den_num)=4*alpha/(pi*sqrt(Ts))*(1+(1-
alpha)*pi/(4*alpha)); end
t1=t(pos_ceros_den1); if length(pos_ceros_den1)>0 pt(pos_ceros_den1)=4*alpha/(pi * sqrt(Ts)); pt(pos_ceros_den1)=pt.*(-sin((1+alpha)*pi*t1/Ts)*(1+alpha)*pi/Ts
+ Ts/4/alpha * (t1*cos((1-alpha)*pi*t1/Ts)*(1-alpha)*pi/Ts-sin((1-
alpha)*pi*t1/Ts))/(t1^2)); pt(pos_ceros_den1)=pt./(-2*(4*alpha*t1/Ts)*(4*alpha/Ts));
end
pt(pos_ceros_den2)=pt(pos_ceros_den1);
Transmisor
%%-----------------------Transmisor DQPSK----------------------------- %____________________Autor:Iván Pinar Domínguez_______________________ %--------------------------------------------------------------------- %%Obtención de los bits del fichero a enviar clear all; load bits_info;%Se cargan los datos previamente guardados %--------------------------------------------------------------------- % CODIFICACIÓN DE CANAL %--------------------------------------------------------------------- t = poly2trellis(3,[3 5]); k = log2(t.numInputSymbols); tblen=3; [datos_c stateA]=convenc([bits_info zeros(1,tblen*k)],t); %Se añaden
ceros de flushing
%--------------------------------------------------------------------- % MODULACIÓN DQPSK %--------------------------------------------------------------------- u=0:3; QPSK_Symb = exp(2*i*pi*((2*u+1)/8)); est_inicial=QPSK_Symb(1);%estado inicial del que partimos est_ant=est_inicial; n=1; for i=1:2:length(datos_c) symb=[datos_c(i) datos_c(i+1)]; if symb==[0 0] symb_QPSK(n)=est_ant; elseif symb==[0 1] symb_QPSK(n)=est_ant*exp(j*pi/2); elseif symb==[1 1] symb_QPSK(n)=est_ant*exp(j*pi); elseif symb==[1 0] symb_QPSK(n)=est_ant*exp(-j*pi/2);
Software Defined Radio: USRP y GNU Radio
126
end est_ant=symb_QPSK(n);%Actualizamos el valor actual de la fase n=n+1;
end plot(real(symb_QPSK),imag(symb_QPSK),'b.'),title('Secuencia de símbolos
QPSK'); pause; close all;
%--------------------------------------------------------------------- %%FILTRO RRC %--------------------------------------------------------------------- ss=16;%muestras por símbolo BW=500e3; Ts=(1/BW)*ss; alpha=0.22; [pt,tp]=pulso(Ts,alpha,ss); symb_QPSK_rem=kron(symb_QPSK,[1 zeros(1,(ss-1))]); sig=conv(symb_QPSK_rem,pt);
h=spectrum.welch; psd(h,sig,'CenterDC',true,'Fs',BW),title('PSD de la señal
transmitida'); pause,close all; sig_n=sig*length(sig)/sum(abs(sig));%Se normaliza la señal a módulo 1 p_inicial=round(length(pt)/2);%punto inicial para representar la
constelación plot(sig_n),hold on,plot(real(sig_n(p_inicial:ss:(end-
2*p_inicial))),imag(sig_n(p_inicial:ss:(end-
2*p_inicial))),'r.'),title('Diagrama vectorial y constelación de la
señal transmitida');
%--------------------------------------------------------------------- %%SEÑAL TRANSMITIDA %--------------------------------------------------------------------- sig_Tx=kron(ones(1,5),[200*ones(1,0.2*BW) zeros(1,0.1*BW) sig
zeros(1,0.1*BW)]);%Se transmiten 5 ráfagas con los mismos datos de 0,8
seg con 0,3 segundo entre ráfagas Te = (1:length(sig_Tx))/BW; figure,subplot(211),plot(Te,real(sig_Tx)),title('Parte en fase de la
señal transmitida'),xlabel('t(s)'); subplot(212),plot(Te,imag(sig_Tx)),title('Parte en cuadratura de la
señal transmitida'),xlabel('t(s)'); pause close all
%--------------------------------------------------------------------- %%TRANSMISIÓN %--------------------------------------------------------------------- input('Pulsar enter para configurar parámetros de transmisión'); sock=SDR4All_Connect(0,'SlotA','TX'); % USRP #0, slot A G=input('Seleccionar ganancia (dB) [0-20]: '); SDR4All_SetGain(sock,G); % Ganancia de transmisión máxima F=input('Seleccionar frecuencia (MHz) [2400-2700]: '); SDR4All_SetFreq(sock,F*1e6); input('Ancho de banda'),BW SDR4All_SetInterpRate(sock,128e6/(BW)); % Tasa de muestras y ancho de
banda de 500 KHz (128MHz/256) input('Pulsar enter para comenzar la transmisión'); SDR4All_SendData(sock,sig_Tx); %---------------------------------------------------------------------
Iván Pinar Domínguez
127
Receptor
%%-----------------------Receptor DQPSK-------------------------------- %__________________Autor:Iván Pinar Domínguez_________________________ %---------------------------------------------------------------------- %---------------------------------------------------------------------- %%RECEPCIÓN %---------------------------------------------------------------------- clear all; input('Pulsar enter para configurar parámetros de recepción'); sock=SDR4All_Connect(0,'SlotB','RX'); % USRP #0, slot B G=input('Seleccionar ganancia (dB) [0-90]: '); SDR4All_SetGain(sock,G); % Ganancia de transmisión máxima F=input('Seleccionar frecuencia (MHz) [2400-2700]: '); SDR4All_SetFreq(sock,F*1e6); BW=500e3; input('Ancho de banda'),BW SDR4All_SetDecimRate(sock,64e6/(BW)); % Tasa de muestras y ancho de
banda de 500 KHz (128MHz/256) input('Pulsar enter para comenzar la recepción'); [sig_Rx] = SDR4All_GetData(sock,10*BW);%10 segundos de señal plot(real(sig_Rx)); pause; %load('sigRX250070dB.mat');%Después de hacer la transmisión sig_Rx=sig_Rx(1000:end);%Elimina posible transitorio inicial
ss=16;%muestras por símbolo Te = (1:length(sig_Rx))/BW; subplot(211),plot(Te,real(sig_Rx)),title('Señal recibida
(I)'),xlabel('t(s)'); subplot(212),plot(Te,imag(sig_Rx)),title('Señal recibida
(Q)'),xlabel('t(s)'); pause close all; %---------------------------------------------------------------------- %%SELECCIÓN DE RÁFAGA %---------------------------------------------------------------------- v_rms=norm(sig_Rx)/sqrt(length(sig_Rx)); int_seg=v_rms*0.0;%intervalo para soportar un pico de interferencia ind_mod=find(abs(sig_Rx)>(v_rms+int_seg)); ind_raf=ind_mod(1); raf_Rx=sig_Rx(ind_raf:ind_raf+1.2*BW); %%se queda con 1.2 s (la ráfaga
de datos está en 0.8s finales) Tr=(1:length(raf_Rx))/BW; figure,subplot(211),plot(Tr,real(raf_Rx)),title('Parte en fase de la
ráfaga seleccionada'),xlabel('t(s)'); subplot(212),plot(Tr,imag(raf_Rx)),title('Parte en cuadratura de la
ráfaga seleccionada'),xlabel('t(s)'); pause,close all;
raf_Rx_datos=raf_Rx(0.3*BW:end); Trd=(1:length(raf_Rx_datos))/BW; figure,subplot(211),plot(Trd,real(raf_Rx_datos)),title('Parte en fase
de la ráfaga de datos'),xlabel('t(s)'); subplot(212),plot(Trd,imag(raf_Rx_datos)),title('Parte en cuadratura de
la ráfaga de datos'),xlabel('t(s)'); pause,close all; h=spectrum.welch; psd(h,raf_Rx_datos(1:(end-0.2*BW)),'CenterDC',true,'Fs',BW),title('PSD
ráfaga Rx'); pause,close all;
Software Defined Radio: USRP y GNU Radio
128
%---------------------------------------------------------------------- %%SINCRONIZACIÓN %----------------------------------------------------------------------
%%%Corrección de frecuencia-------------------------------------------- N=5*BW; f=0:BW/N:(BW-BW/N); psd(h,raf_Rx(1:(0.19*BW)),'CenterDC',true,'Fs',BW),title('PSD ráfaga de
sincronización de frecuencia'); pause,close all; [amp,f_off]=max(abs(fft(raf_Rx(1:0.19*BW),N)));%f_off será el valor del
eje x en número de muestra f_off=f_off*BW/N;%se calcula f_off a partir del número de muestra del
máximo teniendo en cuenta el sobremuestreo raf_Rx_f=raf_Rx_datos.*exp(-j*2*pi*f_off*Trd.'); psd(h,raf_Rx_f(1:(end-0.2*BW)),'CenterDC',true,'Fs',BW),title('PSD
ráfaga sin offset de frecuencia'); pause,close all;
%%%Sincronización en tiempo-------------------------------------------- %Selección del instante de muestreo óptimo en base a la varianza de %la magnitud para cada instante (16 posibilidades) fin_raf=mod(length(raf_Rx_f),16);%Para eliminar las muestras finales y
reshape opere adecuadamente raf_rec=reshape(raf_Rx_f(1:end-fin_raf),16,((length(raf_Rx_f)-
fin_raf)/16)); v=raf_rec.';%reshape va ordenando por columnas y se pretende tener en
cada fila 16 muestras m=abs(v); sigma=var(m);%calcula la varianza de cada una de las 16 columnas plot(sigma),title('Varianza de la magnitud para cada instante de
muestreo'),xlabel('Instante de muestreo óptimo'); pause,close all; [Y,inst_opt]=min(sigma);%el mínimo será el instante de muestreo óptimo
de los 16 posibles
%Se define ya el filtro RRC para calcular la correlación y el instante %óptimo de muestreo Ts=(1/BW)*ss; alpha=0.22; [pt,tp]=pulso(Ts,alpha,ss); num_desp=100; [correlacion,lags]=xcorr(real(raf_Rx_f(1:num_desp)),pt); plot(lags,abs(correlacion)),title('Correlación ráfaga con pulso coseno
alzado'),xlabel('desplazamiento (número de muestras)'),pause,close all; [max_corr,x_max]=max(abs(xcorr(real(raf_Rx_f(1:num_desp)),pt))); desp_max=(x_max-length(pt))+length(pt)/2;%xcorr tiene desplazamientos
negativos y positivos punto_muestreo=inst_opt+floor(desp_max/ss)*ss;%el punto de muestreo
inicial será el el instante de muestreo óptimo más cercano al máximo de
la correlación plot(raf_Rx_f),hold
on,plot(real(raf_Rx_f(punto_muestreo:ss:(BW/2))),imag(raf_Rx_f((punto_m
uestreo):ss:(BW/2))),'r.'),title('Diagrama vectorial y constelación Rx
tras ajustar offset'); pause close all;
%---------------------------------------------------------------------- %%FILTRO ADAPTADO RRC %---------------------------------------------------------------------- raf_Rx_fil=conv(pt,raf_Rx_f(1:0.85*BW)); subplot(211),plot(Tr(1:length(raf_Rx_fil)),real(raf_Rx_fil)),title('Par
te en fase tras filtro adaptado'),xlabel('t(s)');
Iván Pinar Domínguez
129
subplot(212),plot(Tr(1:length(raf_Rx_fil)),imag(raf_Rx_fil)),title('Par
te en cuadratura tras filtro adaptado'),xlabel('t(s)'); pause,close all; %punto_muestreo_fil=punto_muestreo+floor(length(pt)/2);% Será el
anterior %más la mitad de la longitud del pulso coseno alzado---1ª APROXIMACIÓN
%%%%%%%%%%%%%%%% RESINCRONIZACIÓN EN TIEMPO fin_raf=mod(length(raf_Rx_fil),16);%Para eliminar las muestras finales
y reshape opere adecuadamente raf_rec=reshape(raf_Rx_fil(1:end-fin_raf),16,((length(raf_Rx_fil)-
fin_raf)/16)); v=raf_rec.';%reshape va ordenando por columnas y se pretende tener en
cada fila 16 muestras m=abs(v); sigma=var(m);%calcula la varianza de cada una de las 16 columnas plot(sigma),title('Varianza de la magnitud para cada instante de
muestreo'),xlabel('Instante de muestreo óptimo'); pause,close all; [Y,inst_optRRC]=min(sigma);%el mínimo será el instante de muestreo
óptimo de los 16 posibles num_despRRC=200; [correlacionRRC,lags]=xcorr(real(raf_Rx_fil(1:num_despRRC)),pt); plot(lags,abs(correlacionRRC)),title('Correlación ráfaga tras filtro
con pulso coseno alzado'),xlabel('desplazamiento (número de
muestras)'),pause,close all; [max_corr,x_maxRRC]=max(abs(xcorr(real(raf_Rx_fil(1:num_despRRC)),pt)))
; desp_maxRRC=(x_maxRRC-num_despRRC)+length(pt)/2;%xcorr tiene
desplazamientos negativos y positivos punto_muestreo_fil=inst_optRRC+floor(desp_maxRRC/ss)*ss;%el punto de
muestreo inicial será el el instante de muestreo óptimo más cercano al
máximo de la correlación %%%%%%%%%%%%%%%
figure, plot(raf_Rx_fil),hold on,
plot(real(raf_Rx_fil((punto_muestreo_fil):ss:(BW/2))),imag(raf_Rx_fil((
punto_muestreo_fil):ss:(BW/2))),'r.'),title('Constelación tras filtro
adaptado');%El punto inicial será el seleccionado para las
constelaciones anteriores más 81 (length(pt)/2) pause close all;
psd(h,raf_Rx_fil(1:(end-0.2*BW)),'CenterDC',true,'Fs',BW),title('PSD
ráfaga tras filtro adaptado'); pause,close all;
%eyediagram(raf_Rx_fil(punto_muestreo_fil:20000),2*ss),pause,close all;
%---------------------------------------------------------------------- %%DEMODULADOR DQPSK %---------------------------------------------------------------------- r=raf_Rx_fil(punto_muestreo_fil:ss:length(raf_Rx_fil)); est_anterior=abs(real(r(1)))+j*abs(imag(r(1))); n=1; for h=1:length(r) dif1=abs(r(h)-est_anterior); dif2=abs(r(h)-est_anterior*exp(j*pi/2)); dif3=abs(r(h)-est_anterior*exp(j*pi)); dif4=abs(r(h)-est_anterior*exp(-j*pi/2));
vec_dif=[dif1 dif2 dif3 dif4]; [min_dif,pos_min_dif]=min(vec_dif);
Software Defined Radio: USRP y GNU Radio
130
if pos_min_dif==1 datos_dem(n:n+1)=[0 0]; else if pos_min_dif==2 datos_dem(n:n+1)=[0 1]; else if pos_min_dif==3 datos_dem(n:n+1)=[1 1]; else datos_dem(n:n+1)=[1 0]; end end end n=n+2; est_anterior=r(h); end
%---------------------------------------------------------------------- %%Tasa de error bits codificados %---------------------------------------------------------------------- load datos_c;%carga la variable datos_c Pc=sum(abs(datos_dem(1:length(datos_c))-datos_c))/length(datos_c)%Tasa
de error de bits codificados plot(datos_dem(1:length(datos_c))-datos_c),title('Errores en bits
codificados'),axis([0 50006 -2 2]),pause,close all;
%---------------------------------------------------------------------- %%DECODIFICADOR DE CANAL %---------------------------------------------------------------------- t = poly2trellis(3,[3 5]); k = log2(t.numInputSymbols); tblen = 3; [d m p in] = vitdec(datos_c,t,tblen,'cont','hard'); datos_dec=d(tblen*k+1:end);%Se elimina el transitorio inicial
%---------------------------------------------------------------------- %%BER %---------------------------------------------------------------------- load datos_b; BER=sum(abs(datos_dec(1:length(datos_b))-datos_b))/length(datos_b)%Tasa
de error de bits codificados plot(datos_dec(1:length(datos_b))-datos_b),title('Errores en
bits'),axis([0 25000 -2 2]),pause,close all;