Небольшая статья для начала работы на питоне с приемником HackRF One под Windows. Когда мне захотелось работать с приемником HackRF напрямую из Python, я обнаружил, что существующая библиотека pyhackrf
работает только на Linux. Это подтолкнуло меня к доработке подхода для работы под Windows через прямое взаимодействие с DLL. Возможно, мой опыт кому-то пригодится.
Почему напрямую через DLL?
pyhackrf
изначально заточен под Linux и используетlibhackrf.so
На Windows
libhackrf.so
заменяется наhackrf.dll
Прямой вызов DLL через Python позволяет полностью контролировать устройство
Минимальные зависимости и прозрачность работы
Полная свобода в реализации нужного функционала
Создание Python-библиотеки для HackRF
Код hackrf_
dll.py
from ctypes import *
import os
import numpy as np
# Путь к DLL
dll_path = os.path.join(os.path.dirname(__file__), "hackrf.dll")
lib = CDLL(dll_path)
p_hackrf_device = c_void_p
# ==== Прототипы функций ====
lib.hackrf_init.restype = c_int
lib.hackrf_exit.restype = c_int
lib.hackrf_open.argtypes = [POINTER(p_hackrf_device)]
lib.hackrf_open.restype = c_int
lib.hackrf_close.argtypes = [p_hackrf_device]
lib.hackrf_close.restype = c_int
lib.hackrf_set_freq.argtypes = [p_hackrf_device, c_uint64]
lib.hackrf_set_sample_rate.argtypes = [p_hackrf_device, c_double]
lib.hackrf_set_amp_enable.argtypes = [p_hackrf_device, c_uint8]
lib.hackrf_set_lna_gain.argtypes = [p_hackrf_device, c_uint32]
lib.hackrf_set_vga_gain.argtypes = [p_hackrf_device, c_uint32]
# ==== RX структура ====
class hackrf_transfer(Structure):
_fields_ = [
("device", c_void_p),
("buffer", POINTER(c_byte)),
("buffer_length", c_int),
("valid_length", c_int),
("rx_ctx", c_void_p),
("tx_ctx", c_void_p),
]
rx_callback_t = CFUNCTYPE(c_int, POINTER(hackrf_transfer))
tx_callback_t = CFUNCTYPE(c_int, POINTER(hackrf_transfer))
lib.hackrf_start_rx.argtypes = [p_hackrf_device, rx_callback_t, c_void_p]
lib.hackrf_stop_rx.argtypes = [p_hackrf_device]
lib.hackrf_start_tx.argtypes = [p_hackrf_device, tx_callback_t, c_void_p]
lib.hackrf_stop_tx.argtypes = [p_hackrf_device]
# ==== Класс HackRF ====
class HackRF:
def __init__(self):
self.dev = p_hackrf_device(None)
if lib.hackrf_init() != 0:
raise RuntimeError("HackRF init error")
if lib.hackrf_open(pointer(self.dev)) != 0:
raise RuntimeError("HackRF open error")
print("[INFO] HackRF открыт!")
self._rx_cb = None
self._tx_cb = None
# ---- Настройка ----
def set_freq(self, freq_hz):
if lib.hackrf_set_freq(self.dev, int(freq_hz)) != 0:
raise RuntimeError("HackRF set_freq error")
print(f"[INFO] Частота {freq_hz} Hz")
def set_sample_rate(self, rate_hz):
if lib.hackrf_set_sample_rate(self.dev, float(rate_hz)) != 0:
raise RuntimeError("HackRF set_sample_rate error")
print(f"[INFO] Частота дискретизации {rate_hz} Hz")
def enable_amp(self, en=True):
if lib.hackrf_set_amp_enable(self.dev, 1 if en else 0) != 0:
raise RuntimeError("HackRF amp error")
print(f"[INFO] Усилитель {'включен' if en else 'выключен'}")
def set_lna_gain(self, gain):
lib.hackrf_set_lna_gain(self.dev, int(gain))
print(f"[INFO] LNA gain {gain}")
def set_vga_gain(self, gain):
lib.hackrf_set_vga_gain(self.dev, int(gain))
print(f"[INFO] VGA gain {gain}")
# ---- Приём ----
def start_rx(self, callback):
def rx_cb(trans_ptr):
t = trans_ptr.contents
length = t.valid_length
if length <= 0:
print("[RX] Пустой буфер")
return 0
buf = cast(t.buffer, POINTER(c_byte * length)).contents
raw = np.frombuffer(buf, dtype=np.int8, count=length)
iq = raw[0::2].astype(np.float32) + 1j * raw[1::2].astype(np.float32)
print(f"[RX] Получено {len(iq)} сэмплов")
if callback:
callback(iq)
return 0
self._rx_cb = rx_callback_t(rx_cb)
res = lib.hackrf_start_rx(self.dev, self._rx_cb, None)
if res != 0:
raise RuntimeError("hackrf_start_rx error")
print("[INFO] Приём запущен")
def stop_rx(self):
lib.hackrf_stop_rx(self.dev)
print("[INFO] Приём остановлен")
# ---- Передача ----
def start_tx(self, callback):
def tx_cb(trans_ptr):
t = trans_ptr.contents
length = t.buffer_length
if length <= 0:
return 0
buf = (c_byte * length).from_address(addressof(t.buffer.contents))
if callback:
iq = callback(length // 2) # ожидаем комплексные отсчёты I/Q
iq = np.array(iq, dtype=np.complex64)
iq_i = np.clip(np.real(iq), -127, 127).astype(np.int8)
iq_q = np.clip(np.imag(iq), -127, 127).astype(np.int8)
interleaved = np.empty(length, dtype=np.int8)
interleaved[0::2] = iq_i[:len(interleaved)//2]
interleaved[1::2] = iq_q[:len(interleaved)//2]
buf[:len(interleaved)] = interleaved
return 0
self._tx_cb = tx_callback_t(tx_cb)
res = lib.hackrf_start_tx(self.dev, self._tx_cb, None)
if res != 0:
raise RuntimeError("hackrf_start_tx error")
print("[INFO] Передача запущена")
def stop_tx(self):
lib.hackrf_stop_tx(self.dev)
print("[INFO] Передача остановлена")
# ---- Закрытие ----
def close(self):
lib.hackrf_close(self.dev)
lib.hackrf_exit()
print("[INFO] HackRF закрыт")
Пример использования библиотеки
from hackrf_dll import HackRF
import time
# Работа с HackRF через контекстный менеджер
with HackRF() as hackrf:
hackrf.set_freq(433_920_000) # Частота для радиоуправления RC
hackrf.set_lna_gain(16)
hackrf.set_vga_gain(16)
hackrf.enable_amp()
# Простейшая "тестовая передача" — пауза
time.sleep(2)
Итоги
Работа напрямую с
hackrf_dll
даёт полный контроль и кроссплатформенную совместимость в Windows.Мини-библиотека позволяет управлять частотой, усилением и включением передатчика, оставаясь лёгкой и прозрачной.
Полните что включать на передачу без антенны можно спалить передатчик. Так же антенна должна быть правильная для той частоты которую выбрали.