HackRF Guide
RemoteRF supports the HackRF One, a popular open-source software-defined radio peripheral designed and manufactured by Great Scott Gadgets. It is a half-duplex transceiver capable of operating from 1 MHz to 6 GHz with sampling rates up to 20 MSPS (with 8-bit ADC/DAC resolution). This guide covers how to initialize, configure, and stream baseband signals with a reserved HackRF through RemoteRF using Python.
Device Initialization
To interface with a HackRF through RemoteRF, import the device driver from the remoterf library and initialize a hackrf object with the token issued upon making your reservation.
This token is used by the RemoteRF platform to validate your reservation and route subsequent interfacing to the correct HackRF corresponding to that reservation.
from remoterf.drivers.hackrf import *
hackrf = HackRF(token='dUx6lG3I9lA') # token issued when making a reservationOnce initialized, the rest of your Python script follows standard HackRF usage, exactly as if connected locally via USB.
Example Script
The Python script below demonstrates how to configure a HackRF and operate it in both transmit and receive modes via the RemoteRF platform.
Because the HackRF One is a half-duplex transceiver, it alternates sequentially between transmitting and receiving.
In this example, the script first generates a +250 kHz complex sinusoid tone, loads it into the transmit buffer, and transmits it at 915 MHz with a 2 MSPS sample rate.
After stopping transmission, the script switches the HackRF into receive mode, captures baseband complex samples with hackrf.read_samples(), and plots the FFT spectrum.
Note that the device initialization lines described above have already been included in the code below. You should not need to change any of the code below except for the token you received upon making a reservation.
import time
import numpy as np
import matplotlib.pyplot as plt
# token provided when making reservation
TOKEN = "dUx6lG3I9lA"
# the below code can also be used to ensure you have the most
# up-to-date drivers pulled from the RemoteRF server
# from remoterf.drivers import ensure_driver
# ensure_driver(token=TOKEN)
# for interfacing with HackRF through RemoteRF
from remoterf.drivers.hackrf import HackRF
# ---------------------------------------------------------------
# HackRF system parameters.
# ---------------------------------------------------------------
sample_rate = 2e6 # baseband sampling rate (2 MSps), between ~2 MHz and 20 MHz
center_freq_Hz = 915e6 # center RF frequency (915 MHz ISM band), between 1 MHz and 6 GHz
filter_bw_Hz = 1.75e6 # baseband analog filter bandwidth in Hz (1.75 to 28 MHz)
rx_lna_gain = 16 # receive IF LNA gain in dB (0 to 40 dB in 8 dB steps)
rx_vga_gain = 20 # receive baseband VGA gain in dB (0 to 62 dB in 2 dB steps)
tx_vga_gain = 30 # transmit VGA gain in dB (0 to 47 dB in 1 dB steps)
amp_enable = False # front-end RF amplifier (+14 dB gain, True / False)
num_samples = 100_000 # number of samples to transmit / capture
# ---------------------------------------------------------------
# Create transmit signal.
# ---------------------------------------------------------------
tone_freq = 250e3 # tone offset frequency: +250 kHz
t = np.arange(num_samples) / sample_rate # time vector
tx_signal = np.exp(2.0j * np.pi * tone_freq * t).astype(np.complex64) # baseband complex tone
target_amplitude = 0.5 # target digital amplitude (scaled to <= 1.0)
tx_signal_scaled = (tx_signal / np.max([np.abs(np.real(tx_signal)), np.abs(np.imag(tx_signal))])) * target_amplitude
# ---------------------------------------------------------------
# Initialize HackRF object using issued token.
# ---------------------------------------------------------------
hackrf = HackRF(token=TOKEN) # create HackRF object
# Common RF parameters
hackrf.sample_rate = int(sample_rate) # set baseband sampling rate
hackrf.center_freq = int(center_freq_Hz) # set center carrier frequency
hackrf.filter_bandwidth = int(filter_bw_Hz) # set analog filter bandwidth
hackrf.amplifier_on = amp_enable # set front-end RF amplifier state
# ---------------------------------------------------------------
# Mode 1: Transmit with HackRF!
# ---------------------------------------------------------------
hackrf.txvga_gain = int(tx_vga_gain) # set transmit gain
hackrf.load_tx_iq(tx_signal_scaled) # load normalized complex IQ samples into buffer
hackrf.start_tx() # begin transmission over the air
time.sleep(1.0) # allow time for transmission
hackrf.stop_tx() # stop transmission
# ---------------------------------------------------------------
# Mode 2: Receive with HackRF!
# ---------------------------------------------------------------
hackrf.lna_gain = int(rx_lna_gain) # set receive IF LNA gain
hackrf.vga_gain = int(rx_vga_gain) # set receive baseband VGA gain
hackrf.clear_buffer() # clear residual samples from buffer to be safe
rx_signal = hackrf.read_samples(num_samples=num_samples) # capture raw samples from HackRF
# ---------------------------------------------------------------
# Clean up device once done receiving.
# ---------------------------------------------------------------
hackrf.close() # safely end use
# ---------------------------------------------------------------
# Compute FFT and Plot Spectral Response
# ---------------------------------------------------------------
rx_fft = np.abs(np.fft.fftshift(np.fft.fft(rx_signal)))
f = np.linspace(-sample_rate / 2, sample_rate / 2, len(rx_fft))
plt.figure(figsize=(9, 5))
plt.plot(f / 1e3, rx_fft, color="tab:blue")
plt.xlabel("Frequency (kHz)")
plt.ylabel("Magnitude")
plt.title(f"FFT of Received Signal (HackRF One @ {center_freq_Hz / 1e6:.1f} MHz)")
plt.grid(True)
plt.show()Properties and Methods
When interacting with a HackRF through RemoteRF, client scripts communicate with a hackrf object generated dynamically from RemoteRF’s built-in hackrf schema. This interface exposes a curated, schema-exposed subset of pyhackrf2 over the remote RPC connection.
Properties
| Property | Type | Access | Range / Options | Description |
|---|---|---|---|---|
sample_rate |
int |
Read / Write | 2 MSPS to 20 MSPS | Complex baseband sampling rate in samples per second. |
center_freq |
int |
Read / Write | 1 MHz to 6 GHz | Center tuning frequency in Hz. |
filter_bandwidth |
int |
Read / Write | 1.75 MHz to 28 MHz | Baseband analog low-pass filter bandwidth in Hz (automatically rounded to the nearest supported MAX2837 bandwidth). |
lna_gain |
int |
Read / Write | 0 to 40 dB (8 dB steps) | Receive intermediate frequency (IF) Low Noise Amplifier gain in dB (0, 8, 16, 24, 32, 40 dB). |
vga_gain |
int |
Read / Write | 0 to 62 dB (2 dB steps) | Receive baseband Variable Gain Amplifier (VGA) gain in dB. |
txvga_gain |
int |
Read / Write | 0 to 47 dB (1 dB steps) | Transmit Variable Gain Amplifier (TXVGA) gain in dB. |
amplifier_on |
bool |
Read / Write | True, False |
Enables or disables the +14 dB front-end RF amplifier (RX pre-amp / TX post-amp). |
bias_tee_on |
bool |
Read / Write | True, False |
Enables or disables 3.3 V DC power on the antenna port (50 mA maximum) to power active antennas or external LNAs. |
sample_count_limit |
int |
Read / Write | Non-negative integer | Asynchronous receive limit in raw IQ bytes; zero means unlimited. |
Methods
| Method | Arguments | Returns | Description |
|---|---|---|---|
hackrf.read_samples(num_samples=131072) |
num_samples (int, optional) |
numpy.ndarray (complex64) |
Synchronously reads a bounded array of complex IQ samples. Sample values are normalized complex numbers between −1.0 and +1.0. |
hackrf.load_tx_iq(samples) |
samples (array-like) |
int |
Loads baseband IQ samples into the hardware transmit buffer. Accepts normalized complex arrays (with values between −1.0 and +1.0) or native interleaved signed 8-bit integers (−128 to +127). Returns total bytes loaded. |
hackrf.start_tx() |
None | None |
Starts transmitting the IQ waveform previously staged with load_tx_iq(). |
hackrf.stop_tx() |
None | None |
Stops an active transmission and returns the radio to idle state. |
hackrf.clear_buffer() |
None | None |
Clears any captured or queued IQ bytes while the radio is idle. |
hackrf.close() |
None | None |
Safely ends remote session use without closing the server-owned radio. |
hackrf.setup() |
None | None |
Confirms that the remote HackRF device is properly connected and ready for use. |
hackrf.get_serial_no() |
None | str |
Queries and returns the hardware serial number of the connected HackRF device. |
hackrf.enumerate() |
None | str (JSON array) |
Returns attached HackRF serial numbers visible on the server as a JSON-encoded list. |
hackrf.set_freq(value) |
value (int) |
None |
Compatibility alias for setting the center frequency (center_freq) in Hz. |
hackrf.set_sample_rate(value) |
value (int) |
None |
Compatibility alias for setting the complex sample rate (sample_rate) in Hz. |
Tips and Tricks
Common Configuration Template
For quick reference, the following template brings together the most commonly configured RF, filter, buffer, and gain properties for a HackRF session:
# Common HackRF Configuration Template
hackrf.sample_rate = int(2e6) # baseband sampling rate (2 MSps), between ~2 MHz and 20 MHz
hackrf.center_freq = int(915e6) # center RF frequency (915 MHz ISM band), between 1 MHz and 6 GHz
hackrf.filter_bandwidth = int(1.75e6) # baseband analog filter bandwidth in Hz (1.75 to 28 MHz)
hackrf.lna_gain = 16 # receive IF LNA gain in dB (0 to 40 dB in 8 dB steps)
hackrf.vga_gain = 20 # receive baseband VGA gain in dB (0 to 62 dB in 2 dB steps)
hackrf.txvga_gain = 30 # transmit VGA gain in dB (0 to 47 dB in 1 dB steps)
hackrf.amplifier_on = False # front-end RF amplifier (+14 dB gain, True / False)Half-Duplex Operation
The HackRF One is a half-duplex transceiver. Unlike full-duplex SDRs (such as the ADALM-PLUTO), it cannot transmit and receive simultaneously on a single unit. When switching between tasks, halt reception before initiating transmission, and call hackrf.stop_tx() before attempting to read samples.
Transmitting Baseband Signals
To transmit signals with a HackRF, generate your baseband waveform, load the samples into the transmit buffer using hackrf.load_tx_iq(), and start the transmission with hackrf.start_tx():
# Create a complex sinusoid at 100 kHz
N = 100000 # number of samples to transmit
t = np.arange(N) / sample_rate
tx_signal = 0.5 * np.exp(2.0j * np.pi * 100e3 * t) # complex sinusoid at 100 kHz
# Normalize signal to [-1.0, 1.0]
tx_signal_scaled = tx_signal / np.max(np.abs(tx_signal))
# Load samples into HackRF and start transmission
hackrf.load_tx_iq(tx_signal_scaled)
hackrf.start_tx()
# Perform processing or wait while transmission is active
import time
time.sleep(2.0)
# Stop transmission when finished
hackrf.stop_tx()8-Bit Converter Resolution and Sample Scaling
The HackRF transceiver uses 8-bit analog-to-digital (ADC) and digital-to-analog (DAC) converters (Maxim MAX5864).
Received samples returned by hackrf.read_samples() are numpy.complex64 values normalized to the range −1.0 to +1.0.
When passing complex waveforms to hackrf.load_tx_iq(), normalize your array so that sample magnitudes remain within [−1.0, 1.0]. The RemoteRF driver maps these values to signed 8-bit integers (−128 to +127).
Because 8-bit converters provide approximately 48 dB of theoretical dynamic range, careful gain configuration is critical to minimize quantization noise on weak signals while preventing clipping on strong signals.
Gain Staging (LNA, VGA, TXVGA, and RF Amplifier)
The HackRF frontend includes multiple configurable gain blocks:
- LNA Gain (
hackrf.lna_gain): 0 to 40 dB in 8 dB steps (0, 8, 16, 24, 32, 40 dB). Controls the intermediate frequency low-noise amplifier prior to the baseband mixer. - VGA Gain (
hackrf.vga_gain): 0 to 62 dB in 2 dB steps. Controls the baseband variable gain amplifier. - TXVGA Gain (
hackrf.txvga_gain): 0 to 47 dB in 1 dB steps. Adjusts transmit power before the final amplification stage. - RF Amplifier (
hackrf.amplifier_on): Controls a front-end +14 dB amplifier. In RX mode, it acts as a pre-amplifier; in TX mode, it provides an output power boost. It is generally recommended to keepamplifier_on = Falseduring reception unless signals are extremely weak, as enabling it increases the noise figure and increases susceptibility to frontend overload.
Antenna Port Bias-Tee
The HackRF can supply 3.3 V DC power (up to 50 mA) directly through its SMA antenna connector by setting hackrf.bias_tee_on = True.
Only enable the bias-tee when connected to active antennas or external low-noise amplifiers designed for 3.3 V phantom power. Connecting a passive antenna, attenuator, or DC-grounded equipment with the bias-tee enabled can damage the hardware.
Flushing the Receive Buffer
Calling hackrf.clear_buffer() before capturing samples discards any queued or residual IQ bytes that may have accumulated in hardware buffers while the radio was idle:
hackrf.clear_buffer() # clear buffer to be safe
rx_signal = hackrf.read_samples(num_samples=131072)Connecting to Multiple HackRFs at Once
Because an individual HackRF is half-duplex, experiments requiring simultaneous transmission and reception (e.g., transmitting from one device and receiving on another) can be performed by reserving two separate HackRFs and initializing distinct client objects:
hackrf_tx = HackRF(token='dUx6lG3I9lA') # create TX HackRF
hackrf_rx = HackRF(token='ajBMv80jMog') # create RX HackRFSafely Closing the Device
Always call hackrf.close() at the end of your script to halt any ongoing transmission and safely reset remote device state:
hackrf.close() # safely end use