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 One

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 :

Schéma fonctionnel 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.

Circuit imprimé du HackRF One

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 :

Graphique temporel des échantillons prélevés sur HackRF Spectrogramme (frequence en fonction du temps) des échantillons extraits du HackRF