HackRF One en Pythonï
Le HackRF One de Great Scott Gadgets est un SDR USB 2.0 qui peut Ă©mettre ou recevoir de 1 MHz Ă 6 GHz et possĂšde une frĂ©quence dâĂ©chantillonnage de 2 Ă 20 MHz. LancĂ© en 2014, il a bĂ©nĂ©ficiĂ© de plusieurs amĂ©liorations mineures au fil des ans. Câest lâun des rares SDR Ă©conomiques capables dâĂ©mettre jusquâĂ 1 MHz, ce qui le rend idĂ©al pour les applications HF (par exemple, la radioamateur) et les applications Ă plus haute frĂ©quence. Sa puissance dâĂ©mission maximale de 15 dBm est Ă©galement supĂ©rieure Ă celle de la plupart des autres SDR, pour plus de dĂ©tails sur la puissance dâĂ©missionallez allez voir cette page . Il utilise un fonctionnement half-duplex, ce qui signifie quâil est soit en mode Ă©mission, soit en mode rĂ©ception Ă tout moment, et il utilise un convertisseur analogique-numĂ©rique/numĂ©rique-analogique 8 bits.
HackRF Architectureï
Le HackRF est basĂ© sur la puce Analog Devices MAX2839, un Ă©metteur-rĂ©cepteur de 2,3 GHz Ă 2,7 GHz. Conçue initialement pour le WiMAX, elle est associĂ©e Ă une puce frontale RF MAX5864 (qui intĂšgre essentiellement le CAN et le CNA) et Ă un synthĂ©tiseur/VCO large bande RFFC5072 (utilisĂ© pour la conversion de frĂ©quence du signal). Cela contraste avec la plupart des autres SDR Ă©conomiques qui utilisent une seule puce appelĂ©e RFIC. Hormis le rĂ©glage de la frĂ©quence gĂ©nĂ©rĂ©e par le RFFC5072, tous les autres paramĂštres que nous ajusterons, tels que lâattĂ©nuation et le filtrage analogique, seront gĂ©rĂ©s par le MAX2839. Au lieu dâutiliser un FPGA ou un systĂšme sur puce (SoC) comme de nombreux SDR, le HackRF utilise un circuit logique programmable complexe (CPLD) qui sert de simple logique dâinterface, et un microcontrĂŽleur, le LPC4320 basĂ© sur ARM, qui gĂšre tout le traitement numĂ©rique du signal (DSP) embarquĂ© et lâinterface USB avec lâhĂŽte (transfert dâĂ©chantillons IQ dans les deux sens et contrĂŽle des paramĂštres du SDR). Le magnifique schĂ©ma fonctionnel suivant, tirĂ© de Great Scott Gadgets, illustre lâarchitecture de la derniĂšre version du HackRF One :
Le HackRF One est hautement extensible et personnalisable. Ă lâintĂ©rieur du boĂźtier en plastique se trouvent quatre connecteurs (P9, P20, P22, and P28). Les dĂ©tails sont disponibles ici. Notez que 8 broches GPIO et 4 entrĂ©es ADC sont sur le connecteur P20, tandis que les interfaces SPI, I2C, et UART sont sur le connecteur P22. Le connecteur P28 peut ĂȘtre utilisĂ© pour dĂ©clencher/synchroniser les opĂ©rations avec un autre appareil (par exemple un commutateur TR, un amplificateur externe ou un autre HackRF), via lâentrĂ©e et la sortie de dĂ©clenchement, avec un dĂ©ali infĂ©rieur Ă une pĂ©riode dâĂ©chantillonnage.
Lâhorloge utilisĂ©e pour lâoscillateur local (LO) et le convertisseur analogique-numĂ©rique (ADC/DAC) provient soit de lâoscillateur intĂ©grĂ© de 25 MHz, soit dâune rĂ©fĂ©rence externe de 10 MHz fournie via un connecteur SMA. Quelle que soit lâhorloge utilisĂ©e, le HackRF gĂ©nĂšre un signal dâhorloge de 10 MHz sur CLKOUT ; un signal carrĂ© standard de 3,3 V et 10 MHz conçu pour une charge Ă haute impĂ©dance. Le port CLKIN est conçu pour recevoir un signal carrĂ© similaire de 10 MHz et 3,3 V, et le HackRF utilisera lâhorloge dâentrĂ©e au lieu du cristal interne lorsquâun signal dâhorloge est dĂ©tectĂ© (notez que la transition vers ou depuis CLKIN nâa lieu quâau dĂ©but dâune opĂ©ration dâĂ©mission ou de rĂ©ception).
Configuration matĂ©rielle et logicielleï
Le processus dâinstallation du logiciel comporte deux Ă©tapes : nous installerons dâabord la bibliothĂšque principale HackRF de Great Scott Gadgets, puis lâAPI Python.
Installation de la bibliothĂšque du HackRFï
Le code suivant a été testé et fonctionne sous Ubuntu 22.04 (avec le hachage de commit 17f3943 de mars 2025) :
git clone https://github.com/greatscottgadgets/hackrf.git
cd hackrf
git checkout 17f3943
cd host
mkdir build
cd build
cmake ..
make
sudo make install
sudo ldconfig
sudo cp /usr/local/bin/hackrf* /usr/bin/.
AprÚs avoir installé hackrf vous pourrez exécuter les utilitaires suivants :
* hackrf_info - Lire les informations du périphérique HackRF, telles que le numéro de série et la version du firmware.
* hackrf_transfer - Envoyer et recevoir des signaux via HackRF. Les fichiers dâentrĂ©e/sortie sont des Ă©chantillons en quadrature de signaux 8 bits.
* hackrf_sweep - Analyseur de spectre en ligne de commande.
* hackrf_clock - Lire et Ă©crire la configuration dâentrĂ©e et de sortie dâhorloge.
* hackrf_operacake - Configurer le commutateur dâantenne Opera Cake connectĂ© Ă HackRF.
* hackrf_spiflash - Outil permettant dâĂ©crire un nouveau firmware sur HackRF. Voir : Mise Ă jour du firmware.
* hackrf_debug - Lire et écrire les registres et autres paramÚtres de configuration bas niveau pour le débogage.
Si vous utilisez Ubuntu via WSL, cĂŽtĂ© Windows, vous devrez transfĂ©rer le pĂ©riphĂ©rique USB HackRF vers WSL. Pour cela, commencez par installer la derniĂšre version de lâutilitaire usbipd (fichier msi) (ce guide suppose que vous disposez de usbipd-win 4.0.0 ou version ultĂ©rieure), puis ouvrez PowerShell en mode administrateur et exĂ©cutez :
usbipd list
<find the BUSID labeled HackRF One and substitute it in the two commands below>
usbipd bind --busid 1-10
usbipd attach --wsl --busid 1-10
Du cĂŽtĂ© WSL, vous devriez pouvoir exĂ©cuter lsusb et voir un nouvel Ă©lĂ©ment nommĂ© Great Scott Gadgets HackRF One. Notez que vous pouvez ajouter lâoption --auto-attach Ă la commande usbipd attach si vous souhaitez une reconnexion automatique.
Enfin, vous devez ajouter les rĂšgles udev Ă lâaide de la commande suivante :
echo 'ATTR{idVendor}=="1d50", ATTR{idProduct}=="6089", SYMLINK+="hackrf-one-%k", MODE="660", TAG+="uaccess"' | sudo tee /etc/udev/rules.d/53-hackrf.rules
sudo udevadm trigger
DĂ©branchez puis rebranchez votre HackRF One (et rĂ©exĂ©cutez la commande usbipd attach). Notez que jâai rencontrĂ© des problĂšmes dâautorisations avec lâĂ©tape suivante jusquâĂ ce que jâutilise WSL USB Manager cĂŽtĂ© Windows, pour gĂ©rer le transfert vers WSL, qui gĂšre apparemment aussi les rĂšgles udev.
Que vous soyez sous Linux natif ou WSL, vous devriez maintenant pouvoir exécuter hackrf_info et voir quelque chose comme :
hackrf_info version: git-17f39433
libhackrf version: git-17f39433 (0.9)
Found HackRF
Index: 0
Serial number: 00000000000000007687865765a765
Board ID Number: 2 (HackRF One)
Firmware Version: 2024.02.1 (API:1.08)
Part ID Number: 0xa000cb3c 0x004f4762
Hardware Revision: r10
Hardware appears to have been manufactured by Great Scott Gadgets.
Hardware supported by installed firmware: HackRF One
Effectuons Ă©galement un enregistrement IQ de la bande FM, dâune largeur de 10 MHz centrĂ©e sur 100 MHz, et nous enregistrerons 1 million dâĂ©chantillons :
hackrf_transfer -r out.iq -f 100000000 -s 10000000 -n 1000000 -a 0 -l 30 -g 50
Cet utilitaire produit un fichier binaire IQ dâĂ©chantillons int8 (2 octets par Ă©chantillon IQ), qui devrait peser 2 Mo dans notre cas. Si vous ĂȘtes curieux, vous pouvez lire lâenregistrement du signal en Python Ă lâaide du code suivant :
import numpy as np
samples = np.fromfile('out.iq', dtype=np.int8)
samples = samples[::2] + 1j * samples[1::2]
print(len(samples))
print(samples[0:10])
print(np.max(samples))
Si votre valeur maximale est de 127 (ce qui signifie que vous avez saturé le CAN), alors abaissez les deux valeurs de gain à la fin de la commande.
Installation de lâAPI Pythonï
Enfin, nous devons installer les bindings Python HackRF One, maintenues par GvozdevLeonid. Elles ont été testées et fonctionnent correctement sous Ubuntu 22.04 le 11/04/2024 avec la derniÚre version de la branche principale.
sudo apt install libusb-1.0-0-dev
pip install python_hackrf==1.2.7
Nous pouvons tester lâinstallation ci-dessus en exĂ©cutant le code suivant. Sâil nây a pas dâerreurs (il nây aura donc aucune sortie), tout devrait fonctionner correctement !
from python_hackrf import pyhackrf # type: ignore
pyhackrf.pyhackrf_init()
sdr = pyhackrf.pyhackrf_open()
sdr.pyhackrf_set_sample_rate(10e6)
sdr.pyhackrf_set_antenna_enable(False)
sdr.pyhackrf_set_freq(100e6)
sdr.pyhackrf_set_amp_enable(False)
sdr.pyhackrf_set_lna_gain(30) # LNA gain - 0 dB Ă 40 dB par pas de 8 dB
sdr.pyhackrf_set_vga_gain(50) # VGA gain - 0 dB Ă 62 dB par pas de 2 dB
sdr.pyhackrf_close()
Pour un test concret de rĂ©ception dâĂ©chantillons, consultez lâexemple de code ci-dessous.
Gain Tx et Rxï
CĂŽtĂ© rĂ©ceptionï
Le HackRF One possÚde cÎté réception, 3 étages de gain différents :
RF (
amp, soit 0 dB soit 11 dB)IF (
lna, de 0 dB Ă 40 dB par pas de 8 dB)baseband (
vga, de 0 dB Ă 62 dB par pas de 2 dB)
Pour la rĂ©ception de la plupart des signaux, il est recommandĂ© de dĂ©sactiver lâamplificateur RF (0 dB), sauf si le signal est extrĂȘmement faible et quâaucun signal fort nâest prĂ©sent Ă proximitĂ©. Le gain FI (LNA) est lâĂ©tage de gain le plus important Ă rĂ©gler pour optimiser le rapport signal/bruit tout en Ă©vitant la saturation du CAN ; câest le premier bouton Ă ajuster. Le gain de bande de base peut ĂȘtre laissĂ© Ă une valeur relativement Ă©levĂ©e, par exemple, nous le laisserons Ă 50 dB.
CĂŽtĂ© transmissionï
CÎté émission, on trouve deux étages de gain :
RF [soit 0 dB soit 11 dB]
IF [de 0 dB Ă 47 dB par pas de 1 dB]
Vous souhaiterez probablement activer lâamplificateur RF, puis vous pourrez ajuster le gain IF en fonction de vos besoins.
RĂ©ception dâĂ©chantillons IQ en Python avec le HackRFï
Actuellement, le package Python python_hackrf ne comprend aucune fonction pratique pour la rĂ©ception dâĂ©chantillons. Il sâagit simplement dâun ensemble de liaisons Python qui correspondent Ă lâAPI C++ du HackRF. Pour recevoir facilement des donnĂ©es IQ, nous devons utiliser une quantitĂ© de code non nĂ©gligeable. Le package Python est configurĂ© pour utiliser une fonction de rappel afin de recevoir davantage dâĂ©chantillons. Cette fonction doit ĂȘtre initialisĂ©e, mais elle sera automatiquement appelĂ©e dĂšs que de nouveaux Ă©chantillons seront disponibles en provenance du HackRF.
Cette fonction de rappel doit toujours prendre trois arguments spĂ©cifiques et doit renvoyer 0 si nous souhaitons recevoir un autre ensemble dâĂ©chantillons. Dans le code ci-dessous, Ă chaque appel de notre fonction de rappel, nous convertissons les Ă©chantillons au type complexe de NumPy, les mettons Ă lâĂ©chelle de -1 Ă +1, puis les stockons, dans un tableau samples plus grand.
AprĂšs lâexĂ©cution du code ci-dessous, si sur votre graphique temporel, les Ă©chantillons atteignent les limites de lâADC (-1 et +1), rĂ©duisez alors lna_gain de 3 dB jusquâĂ ce que les limites ne soient clairement plus atteintes.
from python_hackrf import pyhackrf # type: ignore
import matplotlib.pyplot as plt
import numpy as np
import time
# These settings should match the hackrf_transfer example used in the textbook, and the resulting waterfall should look about the same
recording_time = 1 # seconds
center_freq = 100e6 # Hz
sample_rate = 10e6
baseband_filter = 7.5e6
lna_gain = 30 # 0 to 40 dB in 8 dB steps
vga_gain = 50 # 0 to 62 dB in 2 dB steps
pyhackrf.pyhackrf_init()
sdr = pyhackrf.pyhackrf_open()
allowed_baseband_filter = pyhackrf.pyhackrf_compute_baseband_filter_bw_round_down_lt(baseband_filter) # calculate the supported bandwidth relative to the desired one
sdr.pyhackrf_set_sample_rate(sample_rate)
sdr.pyhackrf_set_baseband_filter_bandwidth(allowed_baseband_filter)
sdr.pyhackrf_set_antenna_enable(False) # It seems this setting enables or disables power supply to the antenna port. False by default. the firmware auto-disables this after returning to IDLE mode
sdr.pyhackrf_set_freq(center_freq)
sdr.pyhackrf_set_amp_enable(False) # False by default
sdr.pyhackrf_set_lna_gain(lna_gain) # LNA gain - 0 to 40 dB in 8 dB steps
sdr.pyhackrf_set_vga_gain(vga_gain) # VGA gain - 0 to 62 dB in 2 dB steps
print(f'center_freq: {center_freq} sample_rate: {sample_rate} baseband_filter: {allowed_baseband_filter}')
num_samples = int(recording_time * sample_rate)
samples = np.zeros(num_samples, dtype=np.complex64)
last_idx = 0
def rx_callback(device, buffer, buffer_length, valid_length): # this callback function always needs to have these four args
global samples, last_idx
accepted = valid_length // 2
accepted_samples = buffer[:valid_length].astype(np.int8) # -128 to 127
accepted_samples = accepted_samples[0::2] + 1j * accepted_samples[1::2] # Convert to complex type (de-interleave the IQ)
accepted_samples /= 128 # -1 to +1
samples[last_idx: last_idx + accepted] = accepted_samples
last_idx += accepted
return 0
sdr.set_rx_callback(rx_callback)
sdr.pyhackrf_start_rx()
print('is_streaming', sdr.pyhackrf_is_streaming())
time.sleep(recording_time)
sdr.pyhackrf_stop_rx()
sdr.pyhackrf_close()
pyhackrf.pyhackrf_exit()
samples = samples[100000:] # get rid of the first 100k samples just to be safe, due to transients
fft_size = 2048
num_rows = len(samples) // fft_size
spectrogram = np.zeros((num_rows, fft_size))
for i in range(num_rows):
spectrogram[i, :] = 10 * np.log10(np.abs(np.fft.fftshift(np.fft.fft(samples[i * fft_size:(i+1) * fft_size]))) ** 2)
extent = [(center_freq + sample_rate / -2) / 1e6, (center_freq + sample_rate / 2) / 1e6, len(samples) / sample_rate, 0]
plt.figure(0)
plt.imshow(spectrogram, aspect='auto', extent=extent) # type: ignore
plt.xlabel("Frequency [MHz]")
plt.ylabel("Time [s]")
plt.figure(1)
plt.plot(np.real(samples[0:10000]))
plt.plot(np.imag(samples[0:10000]))
plt.xlabel("Samples")
plt.ylabel("Amplitude")
plt.legend(["Real", "Imaginary"])
plt.show()
Lorsque vous utilisez une antenne capable de recevoir la bande FM, vous devriez obtenir un résultat similaire à celui-ci, avec plusieurs stations FM visibles sur le graphique en cascade :