This guide covers verifying the USB/IQ path with Python, running example GNU Radio Companion (.grc) flowgraphs, and reference material for drivers and the vendor protocol.
Use the probe script to confirm the OS stack, WinUSB/udev, and vendor bulk stream before GNU Radio.
- Python 3
- Packages from the repo:
client-sw/host/python/requirements.txt(includespyusband a working libusb backend; on Windows use a libusb-compatible driver on the vendor interface—see driver section below).
Install from the repository root:
py -3 -m pip install -r client-sw/host/python/requirements.txt(Linux/macOS: python3 -m pip install ... as appropriate.)
From the repository root:
python3 client-sw/host/python/hfsdr_probe.py --duration-s 5 --report-s 1On Windows you can use py -3 instead of python3.
- Clock and PLL control requests return valid responses.
- Stream rate is stable and non-zero.
- First decoded IQ samples print.
py -3 client-sw/host/python/hfsdr_probe.py --doctor
py -3 client-sw/host/python/hfsdr_probe.py --list-devices
py -3 client-sw/host/python/hfsdr_probe.py --auto-interface --duration-s 3If the doctor finds the device but cannot claim the interface, the vendor interface is usually not bound to WinUSB yet.
Use GNU Radio Companion (3.10+) with the gr-hfsdr-lib blocks so the flowgraph can open the HFSDR source.
- Install GNU Radio 3.10+.
- Install Python deps:
py -3 -m pip install -r client-sw/host/python/requirements.txt(see gr-hfsdr-lib README).
For Windows, it is suggested to use Radioconda (Tested on Radioconda)
In GNU Radio Companion: File → Open and choose one of:
| Example | Path |
|---|---|
| Spectrum / waterfall style | client-sw/gnuradio/examples/basic_spectrum_iq/basic_spectrum_iq.grc |
| IQ record to file | client-sw/gnuradio/examples/fm_iq_record_to_file.grc |
Use Generate then Execute. You should see live plots updating while the device streams.
If you see No module named 'hfsdr_usb': the Embedded Python Block adds client-sw/host/python to sys.path after it finds the repo root. Either start GNU Radio Companion with working directory = the hfsdr repository root (so discovery finds client-sw/host/python/hfsdr_usb.py), or set the block’s module_root parameter to the absolute path of that repo root (no extra quotes). The packaged HFSDR WebUSB Source block (gr-hfsdr-lib) resolves hfsdr_usb via the library folder automatically.
You can paste from client-sw/gnuradio/examples/embedded_python_block_code.py if you need a fresh Embedded Python Block body.
Baseline values aligned with the web UI intent:
samp_rate = 192000fft_size = 2048wf_update_s = 0.06wf_db_min = -110wf_db_max = -55dc_block_len = 64i_gain = 1.0,q_gain = 1.0,gain_const = 1.0
Display path: embedded source → complex-to-float → per-branch gain → float-to-complex → DC blocker → GUI sinks.
python3 client-sw/gnuradio/examples/hfsdr_webusb_demo.pyExpect spectrum and time sinks to update continuously, no repeated USB timeout loops, and clean stop when the window closes.
Warning - the documentation below is not vetted
This section defines the host-visible protocol for the HFSDR USB vendor interface so tools and GNU Radio blocks can interoperate across Windows/Linux.
- Vendor ID:
0xCAFE - Product ID: dynamic from TinyUSB PID bitmap (
0x4031with current class set) - Vendor interface number:
4(ITF_NUM_VENDOR) - Vendor bulk endpoints:
- IN:
0x85(device → host stream) - OUT:
0x05(host → device, currently echo/debug path)
- IN:
- Transport: USB Bulk transfers (not isochronous)
The stream emitted on endpoint 0x85 is a byte sequence of little-endian 16-bit words copied directly from the I2S RX DMA half-buffer.
- Source buffer type in firmware:
volatile uint16_t[] - Words per DMA half-buffer push:
256 - Bytes per DMA half-buffer push:
512 - Sample structure:
- 24-bit audio from TLV320 is captured in 32-bit I2S slots.
- Each 32-bit slot appears as two 16-bit words in transfer order.
- A stereo frame is 4 words (2 slots), i.e. 8 bytes.
Host-side reconstruction:
- Group stream bytes into little-endian
uint16_twords. - Pair words into 32-bit slots:
slot = (word_hi << 16) | word_lo
- Reinterpret
slotas signed 32-bit (int32), then scale to float:float = slot / 2147483648.0
- Pair adjacent slots into IQ:
I = slot[0],Q = slot[1], repeated.
Current firmware sends raw sample stream without sequence counter or frame marker. Drop detection should use:
- host USB timeout/read-gap monitoring,
- firmware drop counters (
usb_hw_vendor_dropped_words()telemetry), - optional future stream header framing if needed.
Requests use bmRequestType vendor. Recipient can be device (wIndex=0) or interface (wIndex=ITF_NUM_VENDOR); host tools should use interface recipient.
- Direction: IN
- Purpose: fetch WebUSB landing page descriptor URL.
- Direction: IN
- Purpose: fetch Microsoft OS 2.0 descriptor set (
wIndex=7) for WinUSB binding.
- Direction: OUT
wValue = 0wLength = 8- Payload: little-endian
uint64frequency in Hz. - Result: status stage success if Si5351 LO programming succeeds.
- Direction: IN
wValue = 0- Response length: 9 bytes:
- byte
[0]:ErrorStatusfrom firmware (READY/NoREADYenum value) - bytes
[1..8]: little-endianuint64frequency in Hz.
- byte
- Direction: OUT
wValue = 0wLength = 1- Payload: raw TLV320 gain register byte.
- Result: status stage success if TLV320 write succeeds.
- Direction: IN
wValue = 0- Response length: 2 bytes:
- byte
[0]:ErrorStatus - byte
[1]: lock state (0unlocked,1locked)
- byte
- Read endpoint
0x85with transfer sizes that are multiples of 512 bytes. - Use multiple in-flight transfers or a dedicated reader thread.
- Keep USB read and decode separate via a ring buffer for GNU Radio integration.
- Claim only vendor interface
4so CDC/UAC interfaces remain available.
Use after firmware flash and OS driver binding are correct.
From a Python flowgraph shell or custom control script:
set_lo_hz(...)changes tuner LO and observed spectrum shifts.set_gain_raw(...)changes measured signal amplitude.get_pll_locked()returns1during stable operation.
Run for at least 10 minutes:
- No source freezes.
- No sustained growth in
read_errorsfromget_stats(). dropped_iqremains low/acceptable for selected buffer size.
Repeat probe, GNU Radio smoke test, runtime checks, and long-run tests on:
- Windows (WinUSB on vendor interface).
- Linux (udev rule installed).
Record any differences in average stream throughput, USB timeout frequency, and startup reliability.
Use client-sw/gnuradio/examples/basic_spectrum_iq/basic_spectrum_iq.grc and compare to the Web UI if applicable:
- Set same LO frequency and ADC gain in both interfaces.
- Verify DC-centered spectrum shape (carrier at expected offset from center).
- Compare noise floor stability over 10–20 seconds.
- Compare peak prominence for the same RF signal.
- Verify retune response latency feels similar when changing LO.