Небольшая статья для начала работы на питоне с приемником 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. -
Мини-библиотека позволяет управлять частотой, усилением и включением передатчика, оставаясь лёгкой и прозрачной.
Полните что включать на передачу без антенны можно спалить передатчик. Так же антенна должна быть правильная для той частоты которую выбрали.
ссылка на оригинал статьи https://habr.com/ru/articles/944960/
Добавить комментарий