HackRF на Windows: работа через DLL и создание Python-библиотеки

от автора

Небольшая статья для начала работы на питоне с приемником 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/