5. PlutoSDR en Python

The PlutoSDR by Analog Devices

En este cap铆tulo aprenderemos c贸mo usar la API de Python para PlutoSDR, que es un SDR de bajo costo de Analog Devices. Cubriremos los pasos de instalaci贸n de PlutoSDR para ejecutar los controladores/software y luego analizaremos la transmisi贸n y recepci贸n con PlutoSDR en Python.

Instalaci贸n de Software/Drivers Pluto

Configurando la m谩quina virtual

Si bien el c贸digo Python proporcionado en este libro deber铆a funcionar en Windows, Mac y Linux, las instrucciones de instalaci贸n a continuaci贸n son espec铆ficas para Ubuntu 22. Si tiene problemas para instalar el software en su sistema operativo, siga las instrucciones proporcionadas por Analog Devices, Recomiendo instalar una m谩quina virtual Ubuntu 22 y probar las instrucciones a continuaci贸n. Alternativamente, si est谩 en Windows 11, el Subsistema de Windows para Linux (WSL) que usa Ubuntu 22 tiende a funcionar bastante bien y admite gr谩ficos listos para usar.

  1. Instalar y abrir VirtualBox.

  2. Cree una nueva m谩quina virtual. Para el tama帽o de la memoria, recomiendo utilizar el 50% de la RAM de su computadora.

  3. Cree el disco duro virtual, elija VDI y asigne tama帽o din谩micamente. 15 GB deber铆an ser suficientes. Si quieres estar realmente seguro, puedes usar m谩s.

  4. Descarga Ubuntu 22 Desktop .iso- https://ubuntu.com/download/desktop

  5. Inicie la m谩quina virtual. Le pedir谩 medios de instalaci贸n. Elija el archivo .iso del escritorio de Ubuntu 22. Elija 鈥渋nstalar ubuntu鈥, use las opciones predeterminadas y una ventana emergente le advertir谩 sobre los cambios que est谩 a punto de realizar. Pulsa continuar. Elija nombre/contrase帽a y luego espere a que la VM termine de inicializarse. Despu茅s de finalizar, la VM se reiniciar谩, pero debe apagarla despu茅s del reinicio.

  6. Vaya a la configuraci贸n de VM (el 铆cono de ajustes).

  7. En sistema > procesador > elija al menos 3 CPU. Si tiene una tarjeta de video real, en pantalla > memoria de video > elija algo mucho m谩s alto.

  8. Inicie su m谩quina virtual.

  9. Recomiendo instalar adiciones de invitados de VM. Dentro de la m谩quina virtual, vaya a Dispositivos > Insertar CD de Guest Additions > presione ejecutar cuando aparezca un cuadro. Sigue las instrucciones. Reinicie la m谩quina virtual. El portapapeles compartido se puede habilitar a trav茅s de Dispositivos > Portapapeles compartido > Bidireccional.

Conectando el PlutoSDR

  1. Si ejecuta OSX, dentro de OSX, no en la VM, en las preferencias del sistema, habilite las 鈥渆xtensiones del kernel鈥. Luego instale HoRNDIS (es posible que deba reiniciar despu茅s).

  2. Si ejecuta Windows, instale este controlador: https://github.com/analogdevicesinc/plutosdr-m2k-drivers-win/releases/download/v0.7/PlutoSDR-M2k-USB-Drivers.exe

  3. Si ejecuta Linux, no deber铆a tener que hacer nada especial.

  4. Conecte el Pluto a la m谩quina host a trav茅s de USB. Aseg煤rate de usar el puerto USB del medio en el Pluto porque el otro es solo para alimentaci贸n. Al conectar Plut贸n se deber铆a crear un adaptador de red virtual, es decir, Plut贸n aparece como un adaptador Ethernet USB.

  5. En la m谩quina host (no en la VM), abra una terminal o su herramienta de ping preferida y haga ping a 192.168.2.1. Si eso no funciona, detenga y depure la interfaz de red.

  6. Dentro de la VM, abra una nueva terminal

  7. Haga ping a 192.168.2.1. Si eso no funciona, det茅ngase aqu铆 y depure. Mientras hace ping, desconecte su Pluto y aseg煤rese de que el ping se detenga; si contin煤a haciendo ping, entonces hay algo m谩s en esa direcci贸n IP en la red y tendr谩 que cambiar la IP de Pluto (u otro dispositivo) antes de continuar.

  8. Anota la direcci贸n IP del Pluto porque la necesitar谩s cuando empecemos a usar el Pluto en Python.

Instalaci贸n del Driver para el PlutoSDR

Los siguientes comandos de terminal deber铆an compilar e instalar la 煤ltima versi贸n de:

  1. libiio, Biblioteca 鈥渕ultiplataforma鈥 de Analog Device para interfaz de hardware

  2. libad9361-iio, AD9361 es el chip RF espec铆fico dentro del PlutoSDR

  3. pyadi-iio, la API Python de Plut贸n, este es nuestro objetivo final, pero depende de las dos bibliotecas anteriores

sudo apt-get install build-essential git libxml2-dev bison flex libcdk5-dev cmake python3-pip libusb-1.0-0-dev libavahi-client-dev libavahi-common-dev libaio-dev
cd ~
git clone --branch v0.23 https://github.com/analogdevicesinc/libiio.git
cd libiio
mkdir build
cd build
cmake -DPYTHON_BINDINGS=ON ..
make -j$(nproc)
sudo make install
sudo ldconfig

cd ~
git clone https://github.com/analogdevicesinc/libad9361-iio.git
cd libad9361-iio
mkdir build
cd build
cmake ..
make -j$(nproc)
sudo make install

cd ~
git clone --branch v0.0.14 https://github.com/analogdevicesinc/pyadi-iio.git
cd pyadi-iio
pip3 install --upgrade pip
pip3 install -r requirements.txt
sudo python3 setup.py install

Probando los Driver del PlutoSDR

Abra una nueva terminal (en su VM) y escriba los siguientes comandos:

python3
import adi
sdr = adi.Pluto('ip:192.168.2.1') # or whatever your Pluto's IP is
sdr.sample_rate = int(2.5e6)
sdr.rx()

Si llega hasta aqu铆 sin ning煤n error, contin煤e con los siguientes pasos.

Cambiando la direcci贸n IP de Plut贸n

Si por alguna raz贸n la IP predeterminada de 192.168.2.1 no funciona porque ya tienes una subred 192.168.2.0, o porque quieres conectar varios Pluto al mismo tiempo, puedes cambiar la IP siguiendo estos pasos:

  1. Edite el archivo config.txt en el dispositivo de almacenamiento masivo PlutoSDR (es decir, la unidad USB que aparece despu茅s de conectar Pluto). Introduce la nueva IP que desees.

  2. Expulse el dispositivo de almacenamiento masivo (隆no desconecte el Plut贸n!). En Ubuntu 22 hay un s铆mbolo de expulsi贸n al lado del dispositivo PlutoSDR, cuando se mira el explorador de archivos.

  3. Espere unos segundos y luego apague y encienda desconectando el Pluto y volvi茅ndolo a enchufar. Vuelva al config.txt para determinar si sus cambios se guardaron.

Tenga en cuenta que este procedimiento tambi茅n se utiliza para mostrar una imagen de firmware diferente en el Pluto. Para m谩s detalles ver https://wiki.analog.com/university/tools/pluto/users/firmware.

鈥淗ackear鈥 PlutoSDR para aumentar el alcance de RF

El PlutoSDR tiene un rango de frecuencia central y una frecuencia de muestreo limitados, pero el chip subyacente es capaz de alcanzar frecuencias mucho m谩s altas. Siga estos pasos para desbloquear todo el rango de frecuencia del chip. Tenga en cuenta que este proceso lo proporciona Analog Devices, por lo que es el riesgo m谩s bajo posible. La limitaci贸n de frecuencia de PlutoSDR tiene que ver con que Analog Devices 鈥渁grupe鈥 el AD9364 seg煤n estrictos requisitos de rendimiento en las frecuencias m谩s altas. 鈥. Como entusiastas y experimentadores de SDR, no nos preocupan demasiado dichos requisitos de rendimiento.

隆Es hora de hackear! Abra una terminal (ya sea host o VM, no importa):

La contrase帽a predeterminada es analog

Deber铆as ver la pantalla de bienvenida de PlutoSDR. 隆Ahora ha conectado SSH a la CPU ARM en el propio Pluto! Si tiene un Pluto con la versi贸n de firmware 0.31 o inferior, escriba los siguientes comandos en:

fw_setenv attr_name compatible
fw_setenv attr_val ad9364
reboot

Y para uso de 0.32 y superiores:

fw_setenv compatible ad9364
reboot

Ahora deber铆as poder sintonizar hasta 6 GHz y bajar hasta 70 MHz, 隆sin mencionar usar una frecuencia de muestreo de hasta 56 MHz! 隆Hurra!

Recepci贸n

El muestreo utilizando la API Python de PlutoSDR es sencillo. Con cualquier aplicaci贸n SDR sabemos que debemos indicarle la frecuencia central, la frecuencia de muestreo y la ganancia (o si usar el control autom谩tico de ganancia). Puede haber otros detalles, pero esos tres par谩metros son necesarios para que el SDR tenga suficiente informaci贸n para recibir muestras. Algunos SDR tienen un comando que le indica que comience a muestrear, mientras que otros, como Plut贸n, comenzar谩n a muestrear tan pronto como lo inicialice. Una vez que el b煤fer interno del SDR se llena, se descartan las muestras m谩s antiguas. Todas las API de SDR tienen alg煤n tipo de funci贸n de 鈥渞ecibir muestras鈥, y para el Pluto es rx(), que devuelve un lote de muestras. El n煤mero espec铆fico de muestras por lote est谩 definido por el tama帽o del b煤fer establecido de antemano.

El siguiente c贸digo supone que tiene instalada la API Python de Plut贸n. Este c贸digo se inicializa el Pluto, establece la frecuencia de muestreo en 1 MHz, establece la frecuencia central en 100 MHz y establece la ganancia en 70 dB con el control autom谩tico de ganancia desactivado. Tenga en cuenta que normalmente no importa el orden en el que establezca la frecuencia central, la ganancia y la frecuencia de muestreo. En el siguiente fragmento de c贸digo, le decimos al Pluto que queremos que nos d茅 10.000 muestras por llamada a rx(). Imprimimos las primeras 10 muestras.

import numpy as np
import adi

sample_rate = 1e6 # Hz
center_freq = 100e6 # Hz
num_samps = 10000 # number of samples returned per call to rx()

sdr = adi.Pluto()
sdr.gain_control_mode_chan0 = 'manual'
sdr.rx_hardwaregain_chan0 = 70.0 # dB
sdr.rx_lo = int(center_freq)
sdr.sample_rate = int(sample_rate)
sdr.rx_rf_bandwidth = int(sample_rate) # filter width, just set it to the same as sample rate for now
sdr.rx_buffer_size = num_samps

samples = sdr.rx() # receive samples off Pluto
print(samples[0:10])

Por ahora no vamos a hacer nada interesante con estos ejemplos, pero el resto de este libro de texto est谩 lleno de c贸digo Python que funciona en ejemplos de IQ tal como lo recibimos anteriormente.

Ganancia de Recepci贸n

El Pluto se puede configurar para que tenga una ganancia de recepci贸n fija o autom谩tica. Un control autom谩tico de ganancia (AGC) ajustar谩 autom谩ticamente la ganancia de recepci贸n para mantener un nivel de se帽al fuerte (-12 dBFS para cualquiera que tenga curiosidad). AGC no debe confundirse con el convertidor anal贸gico a digital (ADC) que digitaliza la se帽al. T茅cnicamente hablando, AGC es un circuito de retroalimentaci贸n de circuito cerrado que controla la ganancia del amplificador en respuesta a la se帽al recibida. Su objetivo es mantener un nivel de potencia de salida constante a pesar de un nivel de potencia de entrada variable. Normalmente, el AGC ajustar谩 la ganancia para evitar saturar el receptor (es decir, alcanzar el l铆mite superior del rango del ADC) y al mismo tiempo permitir谩 que la se帽al 鈥渓lene鈥 tantos bits de ADC como sea posible.

El circuito integrado de radiofrecuencia, o RFIC, dentro del PlutoSDR tiene un m贸dulo AGC con algunas configuraciones diferentes. (Un RFIC es un chip que funciona como un transceptor: transmite y recibe ondas de radio). Primero, tenga en cuenta que la ganancia de recepci贸n en el Pluto tiene un rango de 0 a 74,5 dB. Cuando est谩 en modo AGC 鈥渕anual鈥, el AGC se apaga y debe indicarle a Pluto qu茅 ganancia de recepci贸n usar, por ejemplo:

sdr.gain_control_mode_chan0 = "manual" # turn off AGC
gain = 50.0 # allowable range is 0 to 74.5 dB
sdr.rx_hardwaregain_chan0 = gain # set receive gain

Si desea habilitar el AGC, debe elegir uno de dos modos:

  1. sdr.gain_control_mode_chan0 = "slow_attack"

  2. sdr.gain_control_mode_chan0 = "fast_attack"

Y con AGC habilitado no proporciona un valor a rx_hardwaregain_chan0. Se ignorar谩 porque el propio Pluto ajusta la ganancia de la se帽al. El Pluto tiene dos modos para AGC: ataque r谩pido y ataque lento, como se muestra en el c贸digo recortado arriba. La diferencia entre los dos es intuitiva, si lo piensas bien. El modo de ataque r谩pido reacciona m谩s r谩pido a las se帽ales. En otras palabras, el valor de ganancia cambiar谩 m谩s r谩pido cuando la se帽al recibida cambie de nivel. Ajustar los niveles de potencia de la se帽al puede ser importante, especialmente para los sistemas d煤plex por divisi贸n de tiempo (TDD) que utilizan la misma frecuencia para transmitir y recibir. Configurar el control de ganancia en modo de ataque r谩pido para este escenario limita la atenuaci贸n de la se帽al. Con cualquiera de los modos, si no hay se帽al presente y solo ruido, el AGC maximizar谩 la configuraci贸n de ganancia; cuando aparece una se帽al, saturar谩 el receptor brevemente, hasta que el AGC pueda reaccionar y reducir la ganancia. Siempre puedes comprobar el nivel de ganancia actual en tiempo real con:

sdr._get_iio_attr('voltage0','hardwaregain', False)

Para obtener m谩s detalles sobre el AGC del Pluto SDR, como por ejemplo c贸mo cambiar la configuraci贸n avanzada del AGC, consulte the 鈥淩X Gain Control鈥 section of this page.

Transmitiendo

Antes de transmitir cualquier se帽al con su Pluto, aseg煤rese de conectar un cable SMA entre el puerto TX de Pluto y cualquier dispositivo que act煤e como receptor. Es importante comenzar siempre transmitiendo a trav茅s de un cable, especialmente mientras aprendes c贸mo transmitir, para asegurarte de que el SDR se comporta como deseas. Mantenga siempre la potencia de transmisi贸n extremadamente baja para no sobrecargar al receptor, ya que el cable no aten煤a la se帽al como lo hace el canal inal谩mbrico. Si posee un atenuador (por ejemplo, 30 dB), ahora ser铆a un buen momento para usarlo. Si no tienes otro SDR o un analizador de espectro que act煤e como receptor, en teor铆a puedes usar el puerto RX en el mismo Pluto, pero puede complicarse. Recomendar铆a adquirir un RTL-SDR de $10 para que act煤e como SDR receptor.

Transmitir es muy similar a recibir, excepto que en lugar de decirle al SDR que reciba una cierta cantidad de muestras, le daremos una cierta cantidad de muestras para transmitir. En lugar de estar configurando rx_lo lo haremos con tx_lo, para especificar en qu茅 frecuencia portadora transmitir. La frecuencia de muestreo se comparte entre RX y TX, por lo que la configuraremos como de costumbre. A continuaci贸n se muestra un ejemplo completo de transmisi贸n, donde generamos una sinusoide a +100 kHz, luego transmitimos la se帽al compleja a una frecuencia portadora de 915 MHz, lo que hace que el receptor vea una portadora a 915,1 MHz. Realmente no hay ninguna raz贸n pr谩ctica para hacer esto, podr铆amos simplemente haber configurado center_freq en 915.1e6 y transmitir una matriz de unos, pero quer铆amos generar muestras complejas con fines de demostraci贸n.

import numpy as np
import adi

sample_rate = 1e6 # Hz
center_freq = 915e6 # Hz

sdr = adi.Pluto("ip:192.168.2.1")
sdr.sample_rate = int(sample_rate)
sdr.tx_rf_bandwidth = int(sample_rate) # filter cutoff, just set it to the same as sample rate
sdr.tx_lo = int(center_freq)
sdr.tx_hardwaregain_chan0 = -50 # Increase to increase tx power, valid range is -90 to 0 dB

N = 10000 # number of samples to transmit at once
t = np.arange(N)/sample_rate
samples = 0.5*np.exp(2.0j*np.pi*100e3*t) # Simulate a sinusoid of 100 kHz, so it should show up at 915.1 MHz at the receiver
samples *= 2**14 # The PlutoSDR expects samples to be between -2^14 and +2^14, not -1 and +1 like some SDRs

# Transmit our batch of samples 100 times, so it should be 1 second worth of samples total, if USB can keep up
for i in range(100):
    sdr.tx(samples) # transmit the batch of samples once

Aqu铆 hay algunas notas sobre este c贸digo. Primero, desea simular sus muestras de IQ para que est茅n entre -1 y 1, pero luego, antes de transmitirlas, tenemos que escalarlas en 2^14 debido a c贸mo Analog Devices implement贸 la funci贸n del tx(). Si no est谩 seguro de cu谩les son sus valores m铆nimos/m谩ximos, simplemente impr铆malos con print(np.min(samples), np.max(samples)) o escriba una declaraci贸n if para asegurarse de que nunca superen 1 o sean inferiores a -1 (asumiendo que el c贸digo viene antes de la escala 2^14). En cuanto a la ganancia de transmisi贸n, el rango es de -90 a 0 dB, por lo que 0 dB es la potencia de transmisi贸n m谩s alta. Siempre queremos comenzar con una potencia de transmisi贸n baja y luego aumentar si es necesario, por lo que tenemos la ganancia configurada en -50 dB de forma predeterminada, que est谩 hacia el extremo bajo. No lo establezca simplemente en 0 dB s贸lo porque su se帽al no aparece; Puede que haya algo m谩s que este mal y no querr谩s quemar tu receptor.

Transmisi贸n de muestras repetidas

Si desea transmitir continuamente el mismo conjunto de muestras repetidas, en lugar de usar un bucle for/ while dentro de Python como hicimos anteriormente, puede decirle al Pluto que lo haga usando solo una l铆nea:

sdr.tx_cyclic_buffer = True # Enable cyclic buffers

Luego transmitir铆as tus muestras como de costumbre: sdr.tx(samples) solo una vez, y el Pluto seguir谩 transmitiendo la se帽al indefinidamente, hasta que se llame al destructor de objetos sdr. Para cambiar las muestras que se transmiten continuamente, no puede simplemente llamar sdr.tx(samples) nuevamente con un nuevo conjunto de muestras, primero debe llamar sdr.tx_destroy_buffer(), entonces llamar a sdr.tx(samples).

Transmitir por aire legalmente

Innumerables veces los estudiantes me han preguntado en qu茅 frecuencias pueden transmitir con una antena (en los Estados Unidos). La respuesta corta es ninguna, hasta donde yo s茅. Por lo general, cuando las personas se帽alan regulaciones espec铆ficas que hablan sobre l铆mites de potencia de transmisi贸n, se refieren a las regulaciones del 鈥淭铆tulo 47, Parte 15鈥 de la FCC (47 CFR 15). Pero esas son regulaciones para los fabricantes que construyen y venden dispositivos que operan en las bandas ISM, y las regulaciones discuten c贸mo deben probarse. Un dispositivo Parte 15 es aquel en el que una persona no necesita una licencia para operar el dispositivo en cualquier espectro que est茅 usando, pero el dispositivo en s铆 debe estar autorizado/certificado para demostrar que est谩 operando siguiendo las regulaciones de la FCC antes de comercializarlo y venderlo. Las regulaciones de la Parte 15 especifican los niveles m谩ximos de potencia de transmisi贸n y recepci贸n para las diferentes partes del espectro, pero nada de eso se aplica realmente a una persona que transmite una se帽al con un SDR o su radio casera. Las 煤nicas regulaciones que pude encontrar relacionadas con radios que en realidad no son productos que se venden fueron espec铆ficas para operar una estaci贸n de radio AM o FM de baja potencia en las bandas AM/FM. Tambi茅n hay una secci贸n sobre 鈥渄ispositivos de fabricaci贸n casera鈥, pero dice espec铆ficamente que no se aplica a nada construido a partir de un kit, y ser铆a exagerado decir que un equipo de transmisi贸n que utiliza un SDR es un dispositivo de fabricaci贸n casera. En resumen, las regulaciones de la FCC no son tan simples como 鈥減uedes transmitir en estas frecuencias s贸lo por debajo de estos niveles de potencia鈥, sino que son un enorme conjunto de reglas destinadas a pruebas y cumplimiento.

Otra forma de verlo ser铆a decir 鈥渂ueno, estos no son dispositivos de la Parte 15, pero sigamos las reglas de la Parte 15 como si lo fueran鈥. Para la banda ISM de 915 MHz, las reglas son que 鈥淟a intensidad de campo de cualquier emisi贸n radiada dentro de la banda de frecuencia especificada no exceder谩 los 500 microvoltios/metro a 30 metros. El l铆mite de emisi贸n en este p谩rrafo se basa en instrumentos de medici贸n que emplean un detector promedio 鈥. Como puede ver, no es tan simple como una potencia m谩xima de transmisi贸n en vatios.

Ahora, si tiene su licencia de radioaficionado (ham), la FCC le permite utilizar ciertas bandas reservadas para la radioafici贸n. Todav铆a hay pautas a seguir y potencias m谩ximas de transmisi贸n, pero al menos estos n煤meros se especifican en vatios de potencia radiada efectiva. Esta infograf铆a muestra qu茅 bandas est谩n disponibles para usar seg煤n su clase de licencia (T茅cnico, General y Extra). Recomendar铆a a cualquier persona interesada en transmitir con SDR que obtenga su licencia de radioaficionado, consulte ARRL鈥檚 Getting Licensed page para mas informaci贸n.

Si alguien tiene m谩s detalles sobre lo que est谩 permitido y lo que no, por favor env铆eme un correo electr贸nico.

Transmitir y recibir simult谩neamente

Usando el truco tx_cyclic_buffer puedes recibir y transmitir f谩cilmente al mismo tiempo, apagando el transmisor y luego recibiendo. El siguiente c贸digo muestra un ejemplo pr谩ctico de c贸mo transmitir una se帽al QPSK en la banda de 915 MHz, recibirla y trazar el PSD.

import numpy as np
import adi
import matplotlib.pyplot as plt

sample_rate = 1e6 # Hz
center_freq = 915e6 # Hz
num_samps = 100000 # number of samples per call to rx()

sdr = adi.Pluto("ip:192.168.2.1")
sdr.sample_rate = int(sample_rate)

# Config Tx
sdr.tx_rf_bandwidth = int(sample_rate) # filter cutoff, just set it to the same as sample rate
sdr.tx_lo = int(center_freq)
sdr.tx_hardwaregain_chan0 = -50 # Increase to increase tx power, valid range is -90 to 0 dB

# Config Rx
sdr.rx_lo = int(center_freq)
sdr.rx_rf_bandwidth = int(sample_rate)
sdr.rx_buffer_size = num_samps
sdr.gain_control_mode_chan0 = 'manual'
sdr.rx_hardwaregain_chan0 = 0.0 # dB, increase to increase the receive gain, but be careful not to saturate the ADC

# Create transmit waveform (QPSK, 16 samples per symbol)
num_symbols = 1000
x_int = np.random.randint(0, 4, num_symbols) # 0 to 3
x_degrees = x_int*360/4.0 + 45 # 45, 135, 225, 315 degrees
x_radians = x_degrees*np.pi/180.0 # sin() and cos() takes in radians
x_symbols = np.cos(x_radians) + 1j*np.sin(x_radians) # this produces our QPSK complex symbols
samples = np.repeat(x_symbols, 16) # 16 samples per symbol (rectangular pulses)
samples *= 2**14 # The PlutoSDR expects samples to be between -2^14 and +2^14, not -1 and +1 like some SDRs

# Start the transmitter
sdr.tx_cyclic_buffer = True # Enable cyclic buffers
sdr.tx(samples) # start transmitting

# Clear buffer just to be safe
for i in range (0, 10):
    raw_data = sdr.rx()

# Receive samples
rx_samples = sdr.rx()
print(rx_samples)

# Stop transmitting
sdr.tx_destroy_buffer()

# Calculate power spectral density (frequency domain version of signal)
psd = np.abs(np.fft.fftshift(np.fft.fft(rx_samples)))**2
psd_dB = 10*np.log10(psd)
f = np.linspace(sample_rate/-2, sample_rate/2, len(psd))

# Plot time domain
plt.figure(0)
plt.plot(np.real(rx_samples[::100]))
plt.plot(np.imag(rx_samples[::100]))
plt.xlabel("Time")

# Plot freq domain
plt.figure(1)
plt.plot(f/1e6, psd_dB)
plt.xlabel("Frequency [MHz]")
plt.ylabel("PSD")
plt.show()

Deber铆a ver algo parecido a esto, suponiendo que tenga las antenas adecuadas o un cable conectado:

../_images/pluto_tx_rx.svg

Es un buen ejercicio para adaptarse lentamente. sdr.tx_hardwaregain_chan0 y sdr.rx_hardwaregain_chan0 para asegurarse de que la se帽al recibida se est茅 debilitando o fortaleciendo como se esperaba.

API de referencia

Para obtener la lista completa de propiedades y funciones de sdr que puede llamar, consulte la pyadi-iio Pluto Python code (AD936X).

Ejercicios de Python

En lugar de proporcionarle c贸digo para ejecutar, he creado varios ejercicios en los que se proporciona el 95% del c贸digo y el c贸digo restante es Python bastante sencillo de crear. Los ejercicios no pretenden ser dif铆ciles. Les falta suficiente c贸digo para hacerte pensar.

Ejercicio 1: determine el rendimiento de su USB

Intentemos recibir muestras del PlutoSDR y, en el proceso, veamos cu谩ntas muestras por segundo podemos enviar a trav茅s de la conexi贸n USB 2.0.

Su tarea es crear un script de Python que determine la tasa de recepci贸n de muestras en Python, es decir, contar las muestras recibidas y realizar un seguimiento del tiempo para calcular la tasa. Luego, intente usar diferentes sample_rate y buffer_size para ver c贸mo impacta la tasa m谩s alta posible.

Tenga en cuenta que si recibe menos muestras por segundo que la frecuencia de muestreo especificada, significa que est谩 perdiendo/eliminando una fracci贸n de muestras, lo que probablemente ocurrir谩 con una frecuencia de muestreo alta. El Pluto s贸lo utiliza USB 2.0.

El siguiente c贸digo actuar谩 como punto de partida pero contiene las instrucciones que necesita para realizar esta tarea.

import numpy as np
import adi
import matplotlib.pyplot as plt
import time

sample_rate = 10e6 # Hz
center_freq = 100e6 # Hz

sdr = adi.Pluto("ip:192.168.2.1")
sdr.sample_rate = int(sample_rate)
sdr.rx_rf_bandwidth = int(sample_rate) # filter cutoff, just set it to the same as sample rate
sdr.rx_lo = int(center_freq)
sdr.rx_buffer_size = 1024 # this is the buffer the Pluto uses to buffer samples
samples = sdr.rx() # receive samples off Pluto

Adem谩s, para calcular el tiempo que tarda algo, puede utilizar el siguiente c贸digo:

start_time = time.time()
# do stuff
end_time = time.time()
print('seconds elapsed:', end_time - start_time)

A continuaci贸n se ofrecen varios consejos para empezar.

Sugerencia 1: deber谩 colocar la l铆nea 鈥渟amples = sdr.rx()鈥 en un bucle que se ejecute muchas veces (por ejemplo, 100 veces). Debe contar cu谩ntas muestras recibe en cada llamada a sdr.rx() mientras realiza un seguimiento de cu谩nto tiempo ha transcurrido.

Sugerencia 2: El hecho de que est茅 calculando muestras por segundo no significa que tenga que realizar exactamente 1 segundo para recibir muestras. Puede dividir la cantidad de muestras que recibi贸 por la cantidad de tiempo que pas贸.

Sugerencia 3: comience en sample_rate = 10e6 como muestra el c贸digo porque esta velocidad es mucho m谩s de lo que USB 2.0 puede admitir. Podr谩s ver cu谩ntos datos pasan. Entonces puedes modificar rx_buffer_size. Hazlo mucho m谩s grande y mira qu茅 pasa. Una vez que tenga un script que funcione y haya manipulado rx_buffer_size, intente ajustar sample_rate. Determine qu茅 tan bajo debe llegar hasta poder recibir el 100% de las muestras en Python (es decir, muestras en un ciclo de trabajo del 100%).

Sugerencia 4: en su bucle donde llama a sdr.rx(), intente hacer lo menos posible para no agregar un retraso adicional en el tiempo de ejecuci贸n. No hagas nada intensivo como imprimir desde dentro del bucle.

Como parte de este ejercicio, obtendr谩 una idea del rendimiento m谩ximo de USB 2.0. Puede buscar en l铆nea para verificar sus hallazgos.

Como beneficio adicional, intente cambiar center_freq y rx_rf_bandwidth para ver si afecta la velocidad con la que puede recibir muestras del Pluto.

Ejercicio 2: crear un espectrograma/cascada

Para este ejercicio, crear谩 un espectrograma, tambi茅n conocido como cascada, como aprendimos al final del capitulo Dominio de la Frecuencia . Un espectrograma es simplemente un conjunto de FFT que se muestran apiladas una encima de otra. En otras palabras, es una imagen en la que un eje representa la frecuencia y el otro eje representa el tiempo.

En el capitulo Dominio de la Frecuencia aprendimos el c贸digo Python para realizar una FFT. Para este ejercicio puedes usar fragmentos de c贸digo del ejercicio anterior, as铆 como un poco de c贸digo Python b谩sico.

Consejos:

  1. Intente configurar sdr.rx_buffer_size al tama帽o de FFT para que siempre realice 1 FFT por cada llamada del sdr.rx().

  2. Cree una matriz 2D para contener todos los resultados de FFT donde cada fila sea 1 FFT. Se puede crear una matriz 2D llena de ceros con: np.zeros((num_rows, fft_size)). Acceda a la fila i de la matriz con: waterfall_2darray[i,:].

  3. plt.imshow() es una forma conveniente de mostrar una matriz 2D. Esta escala el color autom谩ticamente.

Como objetivo ambicioso, muestre el espectrograma en tiempo real.